1 | //===--- CollectMacros.cpp ---------------------------------------*- 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 | #include "CollectMacros.h" |
10 | #include "AST.h" |
11 | #include "Protocol.h" |
12 | #include "SourceCode.h" |
13 | #include "clang/Basic/SourceLocation.h" |
14 | #include "clang/Tooling/Syntax/Tokens.h" |
15 | #include "llvm/ADT/STLExtras.h" |
16 | #include <cstddef> |
17 | |
18 | namespace clang { |
19 | namespace clangd { |
20 | |
21 | CharSourceRange MacroOccurrence::toSourceRange(const SourceManager &SM) const { |
22 | auto MainFile = SM.getMainFileID(); |
23 | return syntax::FileRange(MainFile, StartOffset, EndOffset).toCharRange(SM); |
24 | } |
25 | |
26 | Range MacroOccurrence::toRange(const SourceManager &SM) const { |
27 | return halfOpenToRange(SM, R: toSourceRange(SM)); |
28 | } |
29 | |
30 | void CollectMainFileMacros::add(const Token &MacroNameTok, const MacroInfo *MI, |
31 | bool IsDefinition, bool InIfCondition) { |
32 | if (!InMainFile) |
33 | return; |
34 | auto Loc = MacroNameTok.getLocation(); |
35 | if (Loc.isInvalid() || Loc.isMacroID()) |
36 | return; |
37 | |
38 | assert(isInsideMainFile(Loc, SM)); |
39 | auto Name = MacroNameTok.getIdentifierInfo()->getName(); |
40 | Out.Names.insert(key: Name); |
41 | size_t Start = SM.getFileOffset(SpellingLoc: Loc); |
42 | size_t End = SM.getFileOffset(SpellingLoc: MacroNameTok.getEndLoc()); |
43 | if (auto SID = getSymbolID(MacroName: Name, MI, SM)) |
44 | Out.MacroRefs[SID].push_back(x: {.StartOffset: Start, .EndOffset: End, .IsDefinition: IsDefinition, .InConditionalDirective: InIfCondition}); |
45 | else |
46 | Out.UnknownMacros.push_back(x: {.StartOffset: Start, .EndOffset: End, .IsDefinition: IsDefinition, .InConditionalDirective: InIfCondition}); |
47 | } |
48 | |
49 | void CollectMainFileMacros::FileChanged(SourceLocation Loc, FileChangeReason, |
50 | SrcMgr::CharacteristicKind, FileID) { |
51 | InMainFile = isInsideMainFile(Loc, SM); |
52 | } |
53 | |
54 | void CollectMainFileMacros::MacroExpands(const Token &MacroName, |
55 | const MacroDefinition &MD, |
56 | SourceRange Range, |
57 | const MacroArgs *Args) { |
58 | add(MacroNameTok: MacroName, MI: MD.getMacroInfo()); |
59 | } |
60 | |
61 | void CollectMainFileMacros::MacroUndefined(const clang::Token &MacroName, |
62 | const clang::MacroDefinition &MD, |
63 | const clang::MacroDirective *Undef) { |
64 | add(MacroNameTok: MacroName, MI: MD.getMacroInfo()); |
65 | } |
66 | |
67 | void CollectMainFileMacros::Ifdef(SourceLocation Loc, const Token &MacroName, |
68 | const MacroDefinition &MD) { |
69 | add(MacroNameTok: MacroName, MI: MD.getMacroInfo(), /*IsDefinition=*/false, |
70 | /*InConditionalDirective=*/InIfCondition: true); |
71 | } |
72 | |
73 | void CollectMainFileMacros::Ifndef(SourceLocation Loc, const Token &MacroName, |
74 | const MacroDefinition &MD) { |
75 | add(MacroNameTok: MacroName, MI: MD.getMacroInfo(), /*IsDefinition=*/false, |
76 | /*InConditionalDirective=*/InIfCondition: true); |
77 | } |
78 | |
79 | void CollectMainFileMacros::Elifdef(SourceLocation Loc, const Token &MacroName, |
80 | const MacroDefinition &MD) { |
81 | add(MacroNameTok: MacroName, MI: MD.getMacroInfo(), /*IsDefinition=*/false, |
82 | /*InConditionalDirective=*/InIfCondition: true); |
83 | } |
84 | |
85 | void CollectMainFileMacros::Elifndef(SourceLocation Loc, const Token &MacroName, |
86 | const MacroDefinition &MD) { |
87 | add(MacroNameTok: MacroName, MI: MD.getMacroInfo(), /*IsDefinition=*/false, |
88 | /*InConditionalDirective=*/InIfCondition: true); |
89 | } |
90 | |
91 | void CollectMainFileMacros::Defined(const Token &MacroName, |
92 | const MacroDefinition &MD, |
93 | SourceRange Range) { |
94 | add(MacroNameTok: MacroName, MI: MD.getMacroInfo(), /*IsDefinition=*/false, |
95 | /*InConditionalDirective=*/InIfCondition: true); |
96 | } |
97 | |
98 | void CollectMainFileMacros::SourceRangeSkipped(SourceRange R, |
99 | SourceLocation EndifLoc) { |
100 | if (!InMainFile) |
101 | return; |
102 | Position Begin = sourceLocToPosition(SM, Loc: R.getBegin()); |
103 | Position End = sourceLocToPosition(SM, Loc: R.getEnd()); |
104 | Out.SkippedRanges.push_back(x: Range{.start: Begin, .end: End}); |
105 | } |
106 | |
107 | class CollectPragmaMarks : public PPCallbacks { |
108 | public: |
109 | explicit CollectPragmaMarks(const SourceManager &SM, |
110 | std::vector<clangd::PragmaMark> &Out) |
111 | : SM(SM), Out(Out) {} |
112 | |
113 | void PragmaMark(SourceLocation Loc, StringRef Trivia) override { |
114 | if (isInsideMainFile(Loc, SM)) { |
115 | // FIXME: This range should just cover `XX` in `#pragma mark XX` and |
116 | // `- XX` in `#pragma mark - XX`. |
117 | Position Start = sourceLocToPosition(SM, Loc); |
118 | Position End = {.line: Start.line + 1, .character: 0}; |
119 | Out.emplace_back(args: clangd::PragmaMark{.Rng: {.start: Start, .end: End}, .Trivia: Trivia.str()}); |
120 | } |
121 | } |
122 | |
123 | private: |
124 | const SourceManager &SM; |
125 | std::vector<clangd::PragmaMark> &Out; |
126 | }; |
127 | |
128 | std::unique_ptr<PPCallbacks> |
129 | collectPragmaMarksCallback(const SourceManager &SM, |
130 | std::vector<PragmaMark> &Out) { |
131 | return std::make_unique<CollectPragmaMarks>(args: SM, args&: Out); |
132 | } |
133 | |
134 | void CollectMainFileMacros::MacroDefined(const Token &MacroName, |
135 | const MacroDirective *MD) { |
136 | |
137 | if (!InMainFile) |
138 | return; |
139 | const auto *MI = MD->getMacroInfo(); |
140 | add(MacroNameTok: MacroName, MI: MD->getMacroInfo(), IsDefinition: true); |
141 | if (MI) |
142 | for (const auto &Tok : MI->tokens()) { |
143 | auto *II = Tok.getIdentifierInfo(); |
144 | // Could this token be a reference to a macro? (Not param to this macro). |
145 | if (!II || !II->hadMacroDefinition() || |
146 | llvm::is_contained(Range: MI->params(), Element: II)) |
147 | continue; |
148 | if (const MacroInfo *MI = PP.getMacroInfo(II)) |
149 | add(MacroNameTok: Tok, MI); |
150 | } |
151 | } |
152 | |
153 | } // namespace clangd |
154 | } // namespace clang |
155 | |