Path: blob/main/contrib/llvm-project/clang/lib/Format/TokenAnalyzer.cpp
35233 views
//===--- TokenAnalyzer.cpp - Analyze Token Streams --------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// This file implements an abstract TokenAnalyzer and associated helper10/// classes. TokenAnalyzer can be extended to generate replacements based on11/// an annotated and pre-processed token stream.12///13//===----------------------------------------------------------------------===//1415#include "TokenAnalyzer.h"16#include "AffectedRangeManager.h"17#include "Encoding.h"18#include "FormatToken.h"19#include "FormatTokenLexer.h"20#include "TokenAnnotator.h"21#include "UnwrappedLineParser.h"22#include "clang/Basic/Diagnostic.h"23#include "clang/Basic/DiagnosticOptions.h"24#include "clang/Basic/FileManager.h"25#include "clang/Basic/SourceManager.h"26#include "clang/Format/Format.h"27#include "llvm/ADT/STLExtras.h"28#include "llvm/ADT/SmallVector.h"29#include "llvm/Support/Debug.h"30#include <type_traits>3132#define DEBUG_TYPE "format-formatter"3334namespace clang {35namespace format {3637// FIXME: Instead of printing the diagnostic we should store it and have a38// better way to return errors through the format APIs.39class FatalDiagnosticConsumer : public DiagnosticConsumer {40public:41void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,42const Diagnostic &Info) override {43if (DiagLevel == DiagnosticsEngine::Fatal) {44Fatal = true;45llvm::SmallVector<char, 128> Message;46Info.FormatDiagnostic(Message);47llvm::errs() << Message << "\n";48}49}5051bool fatalError() const { return Fatal; }5253private:54bool Fatal = false;55};5657std::unique_ptr<Environment>58Environment::make(StringRef Code, StringRef FileName,59ArrayRef<tooling::Range> Ranges, unsigned FirstStartColumn,60unsigned NextStartColumn, unsigned LastStartColumn) {61auto Env = std::make_unique<Environment>(Code, FileName, FirstStartColumn,62NextStartColumn, LastStartColumn);63FatalDiagnosticConsumer Diags;64Env->SM.getDiagnostics().setClient(&Diags, /*ShouldOwnClient=*/false);65SourceLocation StartOfFile = Env->SM.getLocForStartOfFile(Env->ID);66for (const tooling::Range &Range : Ranges) {67SourceLocation Start = StartOfFile.getLocWithOffset(Range.getOffset());68SourceLocation End = Start.getLocWithOffset(Range.getLength());69Env->CharRanges.push_back(CharSourceRange::getCharRange(Start, End));70}71// Validate that we can get the buffer data without a fatal error.72Env->SM.getBufferData(Env->ID);73if (Diags.fatalError())74return nullptr;75return Env;76}7778Environment::Environment(StringRef Code, StringRef FileName,79unsigned FirstStartColumn, unsigned NextStartColumn,80unsigned LastStartColumn)81: VirtualSM(new SourceManagerForFile(FileName, Code)), SM(VirtualSM->get()),82ID(VirtualSM->get().getMainFileID()), FirstStartColumn(FirstStartColumn),83NextStartColumn(NextStartColumn), LastStartColumn(LastStartColumn) {}8485TokenAnalyzer::TokenAnalyzer(const Environment &Env, const FormatStyle &Style)86: Style(Style), LangOpts(getFormattingLangOpts(Style)), Env(Env),87AffectedRangeMgr(Env.getSourceManager(), Env.getCharRanges()),88UnwrappedLines(1),89Encoding(encoding::detectEncoding(90Env.getSourceManager().getBufferData(Env.getFileID()))) {91LLVM_DEBUG(92llvm::dbgs() << "File encoding: "93<< (Encoding == encoding::Encoding_UTF8 ? "UTF8" : "unknown")94<< "\n");95LLVM_DEBUG(llvm::dbgs() << "Language: " << getLanguageName(Style.Language)96<< "\n");97}9899std::pair<tooling::Replacements, unsigned>100TokenAnalyzer::process(bool SkipAnnotation) {101tooling::Replacements Result;102llvm::SpecificBumpPtrAllocator<FormatToken> Allocator;103IdentifierTable IdentTable(LangOpts);104FormatTokenLexer Lex(Env.getSourceManager(), Env.getFileID(),105Env.getFirstStartColumn(), Style, Encoding, Allocator,106IdentTable);107ArrayRef<FormatToken *> Toks(Lex.lex());108SmallVector<FormatToken *, 10> Tokens(Toks.begin(), Toks.end());109UnwrappedLineParser Parser(Env.getSourceManager(), Style, Lex.getKeywords(),110Env.getFirstStartColumn(), Tokens, *this,111Allocator, IdentTable);112Parser.parse();113assert(UnwrappedLines.back().empty());114unsigned Penalty = 0;115for (unsigned Run = 0, RunE = UnwrappedLines.size(); Run + 1 != RunE; ++Run) {116const auto &Lines = UnwrappedLines[Run];117LLVM_DEBUG(llvm::dbgs() << "Run " << Run << "...\n");118SmallVector<AnnotatedLine *, 16> AnnotatedLines;119AnnotatedLines.reserve(Lines.size());120121TokenAnnotator Annotator(Style, Lex.getKeywords());122for (const UnwrappedLine &Line : Lines) {123AnnotatedLines.push_back(new AnnotatedLine(Line));124if (!SkipAnnotation)125Annotator.annotate(*AnnotatedLines.back());126}127128std::pair<tooling::Replacements, unsigned> RunResult =129analyze(Annotator, AnnotatedLines, Lex);130131LLVM_DEBUG({132llvm::dbgs() << "Replacements for run " << Run << ":\n";133for (const tooling::Replacement &Fix : RunResult.first)134llvm::dbgs() << Fix.toString() << "\n";135});136for (AnnotatedLine *Line : AnnotatedLines)137delete Line;138139Penalty += RunResult.second;140for (const auto &R : RunResult.first) {141auto Err = Result.add(R);142// FIXME: better error handling here. For now, simply return an empty143// Replacements to indicate failure.144if (Err) {145llvm::errs() << llvm::toString(std::move(Err)) << "\n";146return {tooling::Replacements(), 0};147}148}149}150return {Result, Penalty};151}152153void TokenAnalyzer::consumeUnwrappedLine(const UnwrappedLine &TheLine) {154assert(!UnwrappedLines.empty());155UnwrappedLines.back().push_back(TheLine);156}157158void TokenAnalyzer::finishRun() {159UnwrappedLines.push_back(SmallVector<UnwrappedLine, 16>());160}161162} // end namespace format163} // end namespace clang164165166