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