243 lines · cpp
1//===--- MacroExpander.cpp - Format C++ code --------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file contains the implementation of MacroExpander, which handles macro11/// configuration and expansion while formatting.12///13//===----------------------------------------------------------------------===//14 15#include "Macros.h"16 17#include "Encoding.h"18#include "FormatToken.h"19#include "FormatTokenLexer.h"20#include "clang/Basic/TokenKinds.h"21#include "clang/Format/Format.h"22#include "clang/Lex/HeaderSearch.h"23#include "clang/Lex/Lexer.h"24#include "clang/Lex/PreprocessorOptions.h"25#include "llvm/ADT/StringSet.h"26#include "llvm/Support/ErrorHandling.h"27 28namespace clang {29namespace format {30 31struct MacroExpander::Definition {32 StringRef Name;33 SmallVector<FormatToken *, 8> Params;34 SmallVector<FormatToken *, 8> Body;35 36 // Map from each argument's name to its position in the argument list.37 // With "M(x, y) x + y":38 // x -> 039 // y -> 140 llvm::StringMap<size_t> ArgMap;41 42 bool ObjectLike = true;43};44 45class MacroExpander::DefinitionParser {46public:47 DefinitionParser(ArrayRef<FormatToken *> Tokens) : Tokens(Tokens) {48 assert(!Tokens.empty());49 Current = Tokens[0];50 }51 52 // Parse the token stream and return the corresponding Definition object.53 // Returns an empty definition object with a null-Name on error.54 MacroExpander::Definition parse() {55 if (Current->isNot(tok::identifier))56 return {};57 Def.Name = Current->TokenText;58 nextToken();59 if (Current->is(tok::l_paren)) {60 Def.ObjectLike = false;61 if (!parseParams())62 return {};63 }64 if (!parseExpansion())65 return {};66 67 return Def;68 }69 70private:71 bool parseParams() {72 assert(Current->is(tok::l_paren));73 nextToken();74 while (Current->is(tok::identifier)) {75 Def.Params.push_back(Current);76 Def.ArgMap[Def.Params.back()->TokenText] = Def.Params.size() - 1;77 nextToken();78 if (Current->isNot(tok::comma))79 break;80 nextToken();81 }82 if (Current->isNot(tok::r_paren))83 return false;84 nextToken();85 return true;86 }87 88 bool parseExpansion() {89 if (Current->isNoneOf(tok::equal, tok::eof))90 return false;91 if (Current->is(tok::equal))92 nextToken();93 parseTail();94 return true;95 }96 97 void parseTail() {98 while (Current->isNot(tok::eof)) {99 Def.Body.push_back(Current);100 nextToken();101 }102 Def.Body.push_back(Current);103 }104 105 void nextToken() {106 if (Pos + 1 < Tokens.size())107 ++Pos;108 Current = Tokens[Pos];109 Current->Finalized = true;110 }111 112 size_t Pos = 0;113 FormatToken *Current = nullptr;114 Definition Def;115 ArrayRef<FormatToken *> Tokens;116};117 118MacroExpander::MacroExpander(119 const std::vector<std::string> &Macros, SourceManager &SourceMgr,120 const FormatStyle &Style,121 llvm::SpecificBumpPtrAllocator<FormatToken> &Allocator,122 IdentifierTable &IdentTable)123 : SourceMgr(SourceMgr), Style(Style), Allocator(Allocator),124 IdentTable(IdentTable) {125 for (const std::string &Macro : Macros)126 parseDefinition(Macro);127}128 129MacroExpander::~MacroExpander() = default;130 131void MacroExpander::parseDefinition(const std::string &Macro) {132 Buffers.push_back(133 llvm::MemoryBuffer::getMemBufferCopy(Macro, "<scratch space>"));134 FileID FID = SourceMgr.createFileID(Buffers.back()->getMemBufferRef());135 FormatTokenLexer Lex(SourceMgr, FID, 0, Style, encoding::Encoding_UTF8,136 Allocator, IdentTable);137 const auto Tokens = Lex.lex();138 if (!Tokens.empty()) {139 DefinitionParser Parser(Tokens);140 auto Definition = Parser.parse();141 if (Definition.ObjectLike) {142 ObjectLike[Definition.Name] = std::move(Definition);143 } else {144 FunctionLike[Definition.Name][Definition.Params.size()] =145 std::move(Definition);146 }147 }148}149 150bool MacroExpander::defined(StringRef Name) const {151 return FunctionLike.contains(Name) || ObjectLike.contains(Name);152}153 154bool MacroExpander::objectLike(StringRef Name) const {155 return ObjectLike.contains(Name);156}157 158bool MacroExpander::hasArity(StringRef Name, unsigned Arity) const {159 auto it = FunctionLike.find(Name);160 return it != FunctionLike.end() && it->second.contains(Arity);161}162 163SmallVector<FormatToken *, 8>164MacroExpander::expand(FormatToken *ID,165 std::optional<ArgsList> OptionalArgs) const {166 if (OptionalArgs)167 assert(hasArity(ID->TokenText, OptionalArgs->size()));168 else169 assert(objectLike(ID->TokenText));170 const Definition &Def = OptionalArgs171 ? FunctionLike.find(ID->TokenText)172 ->second.find(OptionalArgs.value().size())173 ->second174 : ObjectLike.find(ID->TokenText)->second;175 ArgsList Args = OptionalArgs ? OptionalArgs.value() : ArgsList();176 SmallVector<FormatToken *, 8> Result;177 // Expand each argument at most once.178 llvm::StringSet<> ExpandedArgs;179 180 // Adds the given token to Result.181 auto pushToken = [&](FormatToken *Tok) {182 Tok->MacroCtx->ExpandedFrom.push_back(ID);183 Result.push_back(Tok);184 };185 186 // If Tok references a parameter, adds the corresponding argument to Result.187 // Returns false if Tok does not reference a parameter.188 auto expandArgument = [&](FormatToken *Tok) -> bool {189 // If the current token references a parameter, expand the corresponding190 // argument.191 if (Tok->isNot(tok::identifier))192 return false;193 if (!ExpandedArgs.insert(Tok->TokenText).second)194 return false;195 auto I = Def.ArgMap.find(Tok->TokenText);196 if (I == Def.ArgMap.end())197 return false;198 // If there are fewer arguments than referenced parameters, treat the199 // parameter as empty.200 // FIXME: Potentially fully abort the expansion instead.201 if (I->getValue() >= Args.size())202 return true;203 for (FormatToken *Arg : Args[I->getValue()]) {204 // A token can be part of a macro argument at multiple levels.205 // For example, with "ID(x) x":206 // in ID(ID(x)), 'x' is expanded first as argument to the inner207 // ID, then again as argument to the outer ID. We keep the macro208 // role the token had from the inner expansion.209 if (!Arg->MacroCtx)210 Arg->MacroCtx = MacroExpansion(MR_ExpandedArg);211 pushToken(Arg);212 }213 return true;214 };215 216 // Expand the definition into Result.217 for (FormatToken *Tok : Def.Body) {218 if (expandArgument(Tok))219 continue;220 // Create a copy of the tokens from the macro body, i.e. were not provided221 // by user code.222 FormatToken *New = new (Allocator.Allocate()) FormatToken;223 New->copyFrom(*Tok);224 assert(!New->MacroCtx);225 // Tokens that are not part of the user code are not formatted.226 New->MacroCtx = MacroExpansion(MR_Hidden);227 pushToken(New);228 }229 assert(!Result.empty() && Result.back()->is(tok::eof));230 if (Result.size() > 1) {231 ++Result[0]->MacroCtx->StartOfExpansion;232 ++Result[Result.size() - 2]->MacroCtx->EndOfExpansion;233 } else {234 // If the macro expansion is empty, mark the start and end.235 Result[0]->MacroCtx->StartOfExpansion = 1;236 Result[0]->MacroCtx->EndOfExpansion = 1;237 }238 return Result;239}240 241} // namespace format242} // namespace clang243