blob: 57934bfc09d976f62a6e5bacc51655ecc2e2f288 [file] [log] [blame]
//===- MacroExpansionContext.h - Macro expansion information ----*- 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
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ANALYSIS_MACROEXPANSIONCONTEXT_H
#define LLVM_CLANG_ANALYSIS_MACROEXPANSIONCONTEXT_H
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
namespace clang {
namespace detail {
class MacroExpansionRangeRecorder;
} // namespace detail
/// MacroExpansionContext tracks the macro expansions processed by the
/// Preprocessor. It means that it can track source locations from a single
/// translation unit. For every macro expansion it can tell you what text will
/// be substituted.
///
/// It was designed to deal with:
/// - regular macros
/// - macro functions
/// - variadic macros
/// - transitive macro expansions
/// - macro redefinition
/// - unbalanced parenthesis
///
/// \code{.c}
/// void bar();
/// #define retArg(x) x
/// #define retArgUnclosed retArg(bar()
/// #define BB CC
/// #define applyInt BB(int)
/// #define CC(x) retArgUnclosed
///
/// void unbalancedMacros() {
/// applyInt );
/// //^~~~~~~~~~^ is the substituted range
/// // Substituted text is "applyInt )"
/// // Expanded text is "bar()"
/// }
///
/// #define expandArgUnclosedCommaExpr(x) (x, bar(), 1
/// #define f expandArgUnclosedCommaExpr
///
/// void unbalancedMacros2() {
/// int x = f(f(1)) )); // Look at the parenthesis!
/// // ^~~~~~^ is the substituted range
/// // Substituted text is "f(f(1))"
/// // Expanded text is "((1,bar(),1,bar(),1"
/// }
/// \endcode
/// \remark Currently we don't respect the whitespaces between expanded tokens,
/// so the output for this example might differ from the -E compiler
/// invocation.
/// \remark All whitespaces are consumed while constructing the expansion.
/// After all identifier a single space inserted to produce a valid C
/// code even if identifier follows an other identifiers such as
/// variable declarations.
/// \remark MacroExpansionContext object must outlive the Preprocessor
/// parameter.
class MacroExpansionContext {
public:
/// Creates a MacroExpansionContext.
/// \remark You must call registerForPreprocessor to set the required
/// onTokenLexed callback and the PPCallbacks.
explicit MacroExpansionContext(const LangOptions &LangOpts);
/// Register the necessary callbacks to the Preprocessor to record the
/// expansion events and the generated tokens. Must ensure that this object
/// outlives the given Preprocessor.
void registerForPreprocessor(Preprocessor &PP);
/// \param MacroExpansionLoc Must be the expansion location of a macro.
/// \return The textual representation of the token sequence which was
/// substituted in place of the macro after the preprocessing.
/// If no macro was expanded at that location, returns llvm::None.
Optional<StringRef> getExpandedText(SourceLocation MacroExpansionLoc) const;
/// \param MacroExpansionLoc Must be the expansion location of a macro.
/// \return The text from the original source code which were substituted by
/// the macro expansion chain from the given location.
/// If no macro was expanded at that location, returns llvm::None.
Optional<StringRef> getOriginalText(SourceLocation MacroExpansionLoc) const;
LLVM_DUMP_METHOD void dumpExpansionRangesToStream(raw_ostream &OS) const;
LLVM_DUMP_METHOD void dumpExpandedTextsToStream(raw_ostream &OS) const;
LLVM_DUMP_METHOD void dumpExpansionRanges() const;
LLVM_DUMP_METHOD void dumpExpandedTexts() const;
private:
friend class detail::MacroExpansionRangeRecorder;
using MacroExpansionText = SmallString<40>;
using ExpansionMap = llvm::DenseMap<SourceLocation, MacroExpansionText>;
using ExpansionRangeMap = llvm::DenseMap<SourceLocation, SourceLocation>;
/// Associates the textual representation of the expanded tokens at the given
/// macro expansion location.
ExpansionMap ExpandedTokens;
/// Tracks which source location was the last affected by any macro
/// substitution starting from a given macro expansion location.
ExpansionRangeMap ExpansionRanges;
Preprocessor *PP = nullptr;
SourceManager *SM = nullptr;
const LangOptions &LangOpts;
/// This callback is called by the preprocessor.
/// It stores the textual representation of the expanded token sequence for a
/// macro expansion location.
void onTokenLexed(const Token &Tok);
};
} // end namespace clang
#endif // LLVM_CLANG_ANALYSIS_MACROEXPANSIONCONTEXT_H