xref: /llvm-project/clang-tools-extra/clangd/CollectMacros.h (revision ee6961dbf13167bf09b602b136d72f72d7c8ff0c)
1 //===--- CollectMacros.h -----------------------------------------*- 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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_COLLECTMACROS_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_COLLECTMACROS_H
11 
12 #include "Protocol.h"
13 #include "SourceCode.h"
14 #include "index/SymbolID.h"
15 #include "clang/Basic/SourceLocation.h"
16 #include "clang/Lex/PPCallbacks.h"
17 #include "clang/Lex/Preprocessor.h"
18 #include "llvm/ADT/DenseMap.h"
19 #include <cstddef>
20 #include <string>
21 
22 namespace clang {
23 namespace clangd {
24 
25 struct MacroOccurrence {
26   // Half-open range (end offset is exclusive) inside the main file.
27   size_t StartOffset;
28   size_t EndOffset;
29 
30   bool IsDefinition;
31   // True if the occurence is used in a conditional directive, e.g. #ifdef MACRO
32   bool InConditionalDirective;
33 
34   Range toRange(const SourceManager &SM) const;
35 };
36 
37 struct MainFileMacros {
38   llvm::StringSet<> Names;
39   llvm::DenseMap<SymbolID, std::vector<MacroOccurrence>> MacroRefs;
40   // Somtimes it is not possible to compute the SymbolID for the Macro, e.g. a
41   // reference to an undefined macro. Store them separately, e.g. for semantic
42   // highlighting.
43   std::vector<MacroOccurrence> UnknownMacros;
44   // Ranges skipped by the preprocessor due to being inactive.
45   std::vector<Range> SkippedRanges;
46 };
47 
48 /// Collects macro references (e.g. definitions, expansions) in the main file.
49 /// It is used to:
50 ///  - collect macros in the preamble section of the main file (in Preamble.cpp)
51 ///  - collect macros after the preamble of the main file (in ParsedAST.cpp)
52 class CollectMainFileMacros : public PPCallbacks {
53 public:
54   explicit CollectMainFileMacros(const Preprocessor &PP, MainFileMacros &Out)
55       : SM(PP.getSourceManager()), PP(PP), Out(Out) {}
56 
57   void FileChanged(SourceLocation Loc, FileChangeReason,
58                    SrcMgr::CharacteristicKind, FileID) override;
59 
60   void MacroDefined(const Token &MacroName, const MacroDirective *MD) override;
61 
62   void MacroExpands(const Token &MacroName, const MacroDefinition &MD,
63                     SourceRange Range, const MacroArgs *Args) override;
64 
65   void MacroUndefined(const clang::Token &MacroName,
66                       const clang::MacroDefinition &MD,
67                       const clang::MacroDirective *Undef) override;
68 
69   void Ifdef(SourceLocation Loc, const Token &MacroName,
70              const MacroDefinition &MD) override;
71   void Ifndef(SourceLocation Loc, const Token &MacroName,
72               const MacroDefinition &MD) override;
73   using PPCallbacks::Elifdef;
74   using PPCallbacks::Elifndef;
75   void Elifdef(SourceLocation Loc, const Token &MacroNameTok,
76                const MacroDefinition &MD) override;
77   void Elifndef(SourceLocation Loc, const Token &MacroNameTok,
78                 const MacroDefinition &MD) override;
79 
80   void Defined(const Token &MacroName, const MacroDefinition &MD,
81                SourceRange Range) override;
82 
83   void SourceRangeSkipped(SourceRange R, SourceLocation EndifLoc) override;
84 
85   // Called when the AST build is done to disable further recording
86   // of macros by this class. This is needed because some clang-tidy
87   // checks can trigger PP callbacks by calling directly into the
88   // preprocessor. Such calls are not interleaved with FileChanged()
89   // in the expected way, leading this class to erroneously process
90   // macros that are not in the main file.
91   void doneParse() { InMainFile = false; }
92 
93 private:
94   void add(const Token &MacroNameTok, const MacroInfo *MI,
95            bool IsDefinition = false, bool InConditionalDirective = false);
96   const SourceManager &SM;
97   const Preprocessor &PP;
98   bool InMainFile = true;
99   MainFileMacros &Out;
100 };
101 
102 /// Represents a `#pragma mark` in the main file.
103 ///
104 /// There can be at most one pragma mark per line.
105 struct PragmaMark {
106   Range Rng;
107   std::string Trivia;
108 };
109 
110 /// Collect all pragma marks from the main file.
111 std::unique_ptr<PPCallbacks>
112 collectPragmaMarksCallback(const SourceManager &, std::vector<PragmaMark> &Out);
113 
114 } // namespace clangd
115 } // namespace clang
116 
117 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_COLLECTMACROS_H
118