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