Path: blob/main/contrib/llvm-project/clang/lib/Format/MacroExpander.cpp
35232 views
//===--- MacroExpander.cpp - Format C++ code --------------------*- 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 contains the implementation of MacroExpander, which handles macro10/// configuration and expansion while formatting.11///12//===----------------------------------------------------------------------===//1314#include "Macros.h"1516#include "Encoding.h"17#include "FormatToken.h"18#include "FormatTokenLexer.h"19#include "clang/Basic/TokenKinds.h"20#include "clang/Format/Format.h"21#include "clang/Lex/HeaderSearch.h"22#include "clang/Lex/HeaderSearchOptions.h"23#include "clang/Lex/Lexer.h"24#include "clang/Lex/ModuleLoader.h"25#include "clang/Lex/Preprocessor.h"26#include "clang/Lex/PreprocessorOptions.h"27#include "llvm/ADT/StringSet.h"28#include "llvm/Support/ErrorHandling.h"2930namespace clang {31namespace format {3233struct MacroExpander::Definition {34StringRef Name;35SmallVector<FormatToken *, 8> Params;36SmallVector<FormatToken *, 8> Body;3738// Map from each argument's name to its position in the argument list.39// With "M(x, y) x + y":40// x -> 041// y -> 142llvm::StringMap<size_t> ArgMap;4344bool ObjectLike = true;45};4647class MacroExpander::DefinitionParser {48public:49DefinitionParser(ArrayRef<FormatToken *> Tokens) : Tokens(Tokens) {50assert(!Tokens.empty());51Current = Tokens[0];52}5354// Parse the token stream and return the corresponding Definition object.55// Returns an empty definition object with a null-Name on error.56MacroExpander::Definition parse() {57if (Current->isNot(tok::identifier))58return {};59Def.Name = Current->TokenText;60nextToken();61if (Current->is(tok::l_paren)) {62Def.ObjectLike = false;63if (!parseParams())64return {};65}66if (!parseExpansion())67return {};6869return Def;70}7172private:73bool parseParams() {74assert(Current->is(tok::l_paren));75nextToken();76while (Current->is(tok::identifier)) {77Def.Params.push_back(Current);78Def.ArgMap[Def.Params.back()->TokenText] = Def.Params.size() - 1;79nextToken();80if (Current->isNot(tok::comma))81break;82nextToken();83}84if (Current->isNot(tok::r_paren))85return false;86nextToken();87return true;88}8990bool parseExpansion() {91if (!Current->isOneOf(tok::equal, tok::eof))92return false;93if (Current->is(tok::equal))94nextToken();95parseTail();96return true;97}9899void parseTail() {100while (Current->isNot(tok::eof)) {101Def.Body.push_back(Current);102nextToken();103}104Def.Body.push_back(Current);105}106107void nextToken() {108if (Pos + 1 < Tokens.size())109++Pos;110Current = Tokens[Pos];111Current->Finalized = true;112}113114size_t Pos = 0;115FormatToken *Current = nullptr;116Definition Def;117ArrayRef<FormatToken *> Tokens;118};119120MacroExpander::MacroExpander(121const std::vector<std::string> &Macros, SourceManager &SourceMgr,122const FormatStyle &Style,123llvm::SpecificBumpPtrAllocator<FormatToken> &Allocator,124IdentifierTable &IdentTable)125: SourceMgr(SourceMgr), Style(Style), Allocator(Allocator),126IdentTable(IdentTable) {127for (const std::string &Macro : Macros)128parseDefinition(Macro);129}130131MacroExpander::~MacroExpander() = default;132133void MacroExpander::parseDefinition(const std::string &Macro) {134Buffers.push_back(135llvm::MemoryBuffer::getMemBufferCopy(Macro, "<scratch space>"));136FileID FID = SourceMgr.createFileID(Buffers.back()->getMemBufferRef());137FormatTokenLexer Lex(SourceMgr, FID, 0, Style, encoding::Encoding_UTF8,138Allocator, IdentTable);139const auto Tokens = Lex.lex();140if (!Tokens.empty()) {141DefinitionParser Parser(Tokens);142auto Definition = Parser.parse();143if (Definition.ObjectLike) {144ObjectLike[Definition.Name] = std::move(Definition);145} else {146FunctionLike[Definition.Name][Definition.Params.size()] =147std::move(Definition);148}149}150}151152bool MacroExpander::defined(StringRef Name) const {153return FunctionLike.contains(Name) || ObjectLike.contains(Name);154}155156bool MacroExpander::objectLike(StringRef Name) const {157return ObjectLike.contains(Name);158}159160bool MacroExpander::hasArity(StringRef Name, unsigned Arity) const {161auto it = FunctionLike.find(Name);162return it != FunctionLike.end() && it->second.contains(Arity);163}164165SmallVector<FormatToken *, 8>166MacroExpander::expand(FormatToken *ID,167std::optional<ArgsList> OptionalArgs) const {168if (OptionalArgs)169assert(hasArity(ID->TokenText, OptionalArgs->size()));170else171assert(objectLike(ID->TokenText));172const Definition &Def = OptionalArgs173? FunctionLike.find(ID->TokenText)174->second.find(OptionalArgs.value().size())175->second176: ObjectLike.find(ID->TokenText)->second;177ArgsList Args = OptionalArgs ? OptionalArgs.value() : ArgsList();178SmallVector<FormatToken *, 8> Result;179// Expand each argument at most once.180llvm::StringSet<> ExpandedArgs;181182// Adds the given token to Result.183auto pushToken = [&](FormatToken *Tok) {184Tok->MacroCtx->ExpandedFrom.push_back(ID);185Result.push_back(Tok);186};187188// If Tok references a parameter, adds the corresponding argument to Result.189// Returns false if Tok does not reference a parameter.190auto expandArgument = [&](FormatToken *Tok) -> bool {191// If the current token references a parameter, expand the corresponding192// argument.193if (Tok->isNot(tok::identifier) || ExpandedArgs.contains(Tok->TokenText))194return false;195ExpandedArgs.insert(Tok->TokenText);196auto I = Def.ArgMap.find(Tok->TokenText);197if (I == Def.ArgMap.end())198return false;199// If there are fewer arguments than referenced parameters, treat the200// parameter as empty.201// FIXME: Potentially fully abort the expansion instead.202if (I->getValue() >= Args.size())203return true;204for (FormatToken *Arg : Args[I->getValue()]) {205// A token can be part of a macro argument at multiple levels.206// For example, with "ID(x) x":207// in ID(ID(x)), 'x' is expanded first as argument to the inner208// ID, then again as argument to the outer ID. We keep the macro209// role the token had from the inner expansion.210if (!Arg->MacroCtx)211Arg->MacroCtx = MacroExpansion(MR_ExpandedArg);212pushToken(Arg);213}214return true;215};216217// Expand the definition into Result.218for (FormatToken *Tok : Def.Body) {219if (expandArgument(Tok))220continue;221// Create a copy of the tokens from the macro body, i.e. were not provided222// by user code.223FormatToken *New = new (Allocator.Allocate()) FormatToken;224New->copyFrom(*Tok);225assert(!New->MacroCtx);226// Tokens that are not part of the user code are not formatted.227New->MacroCtx = MacroExpansion(MR_Hidden);228pushToken(New);229}230assert(Result.size() >= 1 && Result.back()->is(tok::eof));231if (Result.size() > 1) {232++Result[0]->MacroCtx->StartOfExpansion;233++Result[Result.size() - 2]->MacroCtx->EndOfExpansion;234}235return Result;236}237238} // namespace format239} // namespace clang240241242