38
38
#include " llvm/ADT/DenseSet.h"
39
39
#include " llvm/ADT/STLExtras.h"
40
40
#include " llvm/ADT/SmallVector.h"
41
+ #include " llvm/Support/FileSystem.h"
41
42
#include " llvm/Support/MemoryBuffer.h"
43
+ #include " llvm/Support/Path.h"
44
+ #include " llvm/Support/Regex.h"
42
45
#include " llvm/Support/raw_ostream.h"
43
46
#include < memory>
44
47
#include < utility>
@@ -55,10 +58,125 @@ StringRef getTypedefName(const TagDecl *Decl) {
55
58
return {};
56
59
}
57
60
61
+ Optional<std::string> getRelativeIncludeName (const CompilerInstance &CI,
62
+ StringRef File,
63
+ bool *IsQuoted = nullptr ) {
64
+ assert (CI.hasFileManager () &&
65
+ " CompilerInstance does not have a FileNamager!" );
66
+
67
+ using namespace llvm ::sys;
68
+ // Matches framework include patterns
69
+ const llvm::Regex Rule (" /(.+)\\ .framework/(.+)?Headers/(.+)" );
70
+
71
+ const auto &FS = CI.getVirtualFileSystem ();
72
+
73
+ SmallString<128 > FilePath (File.begin (), File.end ());
74
+ FS.makeAbsolute (FilePath);
75
+ path::remove_dots (FilePath, true );
76
+ FilePath = path::convert_to_slash (FilePath);
77
+ File = FilePath;
78
+
79
+ // Checks whether `Dir` is a strict path prefix of `File`. If so returns
80
+ // the prefix length. Otherwise return 0.
81
+ auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
82
+ llvm::SmallString<32 > DirPath (Dir.begin (), Dir.end ());
83
+ FS.makeAbsolute (DirPath);
84
+ path::remove_dots (DirPath, true );
85
+ Dir = DirPath;
86
+ for (auto NI = path::begin (File), NE = path::end (File),
87
+ DI = path::begin (Dir), DE = path::end (Dir);
88
+ /* termination condition in loop*/ ; ++NI, ++DI) {
89
+ // '.' components in File are ignored.
90
+ while (NI != NE && *NI == " ." )
91
+ ++NI;
92
+ if (NI == NE)
93
+ break ;
94
+
95
+ // '.' components in Dir are ignored.
96
+ while (DI != DE && *DI == " ." )
97
+ ++DI;
98
+
99
+ // Dir is a prefix of File, up to '.' components and choice of path
100
+ // separators.
101
+ if (DI == DE)
102
+ return NI - path::begin (File);
103
+
104
+ // Consider all path separators equal.
105
+ if (NI->size () == 1 && DI->size () == 1 &&
106
+ path::is_separator (NI->front ()) && path::is_separator (DI->front ()))
107
+ continue ;
108
+
109
+ // Special case Apple .sdk folders since the search path is typically a
110
+ // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
111
+ // located in `iPhoneSimulator.sdk` (the real folder).
112
+ if (NI->endswith (" .sdk" ) && DI->endswith (" .sdk" )) {
113
+ StringRef NBasename = path::stem (*NI);
114
+ StringRef DBasename = path::stem (*DI);
115
+ if (DBasename.startswith (NBasename))
116
+ continue ;
117
+ }
118
+
119
+ if (*NI != *DI)
120
+ break ;
121
+ }
122
+ return 0 ;
123
+ };
124
+
125
+ unsigned PrefixLength = 0 ;
126
+
127
+ // Go through the search paths and find the first one that is a prefix of
128
+ // the header.
129
+ for (const auto &Entry : CI.getHeaderSearchOpts ().UserEntries ) {
130
+ // Note whether the match is found in a quoted entry.
131
+ if (IsQuoted)
132
+ *IsQuoted = Entry.Group == frontend::Quoted;
133
+
134
+ if (auto EntryFile = CI.getFileManager ().getOptionalFileRef (Entry.Path )) {
135
+ if (auto HMap = HeaderMap::Create (*EntryFile, CI.getFileManager ())) {
136
+ // If this is a headermap entry, try to reverse lookup the full path
137
+ // for a spelled name before mapping.
138
+ StringRef SpelledFilename = HMap->reverseLookupFilename (File);
139
+ if (!SpelledFilename.empty ())
140
+ return SpelledFilename.str ();
141
+
142
+ // No matching mapping in this headermap, try next search entry.
143
+ continue ;
144
+ }
145
+ }
146
+
147
+ // Entry is a directory search entry, try to check if it's a prefix of File.
148
+ PrefixLength = CheckDir (Entry.Path );
149
+ if (PrefixLength > 0 ) {
150
+ // The header is found in a framework path, construct the framework-style
151
+ // include name `<Framework/Header.h>`
152
+ if (Entry.IsFramework ) {
153
+ SmallVector<StringRef, 4 > Matches;
154
+ Rule.match (File, &Matches);
155
+ // Returned matches are always in stable order.
156
+ if (Matches.size () != 4 )
157
+ return None;
158
+
159
+ return path::convert_to_slash (
160
+ (Matches[1 ].drop_front (Matches[1 ].rfind (' /' ) + 1 ) + " /" +
161
+ Matches[3 ])
162
+ .str ());
163
+ }
164
+
165
+ // The header is found in a normal search path, strip the search path
166
+ // prefix to get an include name.
167
+ return path::convert_to_slash (File.drop_front (PrefixLength));
168
+ }
169
+ }
170
+
171
+ // Couldn't determine a include name, use full path instead.
172
+ return None;
173
+ }
174
+
58
175
struct LocationFileChecker {
59
176
bool isLocationInKnownFile (SourceLocation Loc) {
60
177
// If the loc refers to a macro expansion we need to first get the file
61
178
// location of the expansion.
179
+ auto &SM = CI.getSourceManager ();
62
180
auto FileLoc = SM.getFileLoc (Loc);
63
181
FileID FID = SM.getFileID (FileLoc);
64
182
if (FID.isInvalid ())
@@ -71,20 +189,44 @@ struct LocationFileChecker {
71
189
if (KnownFileEntries.count (File))
72
190
return true ;
73
191
192
+ if (ExternalFileEntries.count (File))
193
+ return false ;
194
+
195
+ StringRef FileName = File->tryGetRealPathName ().empty ()
196
+ ? File->getName ()
197
+ : File->tryGetRealPathName ();
198
+
199
+ // Try to reduce the include name the same way we tried to include it.
200
+ bool IsQuoted = false ;
201
+ if (auto IncludeName = getRelativeIncludeName (CI, FileName, &IsQuoted))
202
+ if (llvm::find_if (KnownFiles,
203
+ [&IsQuoted, &IncludeName](const auto &KnownFile) {
204
+ return KnownFile.first .equals (*IncludeName) &&
205
+ KnownFile.second == IsQuoted;
206
+ }) != KnownFiles.end ()) {
207
+ KnownFileEntries.insert (File);
208
+ return true ;
209
+ }
210
+
211
+ // Record that the file was not found to avoid future reverse lookup for
212
+ // the same file.
213
+ ExternalFileEntries.insert (File);
74
214
return false ;
75
215
}
76
216
77
- LocationFileChecker (const SourceManager &SM ,
78
- const std::vector <std::string > &KnownFiles)
79
- : SM(SM ) {
80
- for (const auto &KnownFilePath : KnownFiles)
81
- if (auto FileEntry = SM .getFileManager ().getFile (KnownFilePath ))
217
+ LocationFileChecker (const CompilerInstance &CI ,
218
+ SmallVector <std::pair<SmallString< 32 >, bool > > &KnownFiles)
219
+ : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries( ) {
220
+ for (const auto &KnownFile : KnownFiles)
221
+ if (auto FileEntry = CI .getFileManager ().getFile (KnownFile. first ))
82
222
KnownFileEntries.insert (*FileEntry);
83
223
}
84
224
85
225
private:
86
- const SourceManager &SM;
226
+ const CompilerInstance &CI;
227
+ SmallVector<std::pair<SmallString<32 >, bool >> &KnownFiles;
87
228
llvm::DenseSet<const FileEntry *> KnownFileEntries;
229
+ llvm::DenseSet<const FileEntry *> ExternalFileEntries;
88
230
};
89
231
90
232
// / The RecursiveASTVisitor to traverse symbol declarations and collect API
@@ -743,8 +885,7 @@ ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
743
885
CI.getTarget ().getTriple (),
744
886
CI.getFrontendOpts ().Inputs .back ().getKind ().getLanguage ());
745
887
746
- auto LCF = std::make_unique<LocationFileChecker>(CI.getSourceManager (),
747
- KnownInputFiles);
888
+ auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
748
889
749
890
CI.getPreprocessor ().addPPCallbacks (std::make_unique<MacroCallback>(
750
891
CI.getSourceManager (), *LCF, *API, CI.getPreprocessor ()));
@@ -758,22 +899,47 @@ bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
758
899
if (Inputs.empty ())
759
900
return true ;
760
901
902
+ if (!CI.hasFileManager ())
903
+ if (!CI.createFileManager ())
904
+ return false ;
905
+
761
906
auto Kind = Inputs[0 ].getKind ();
762
907
763
908
// Convert the header file inputs into a single input buffer.
764
909
SmallString<256 > HeaderContents;
910
+ bool IsQuoted = false ;
765
911
for (const FrontendInputFile &FIF : Inputs) {
766
912
if (Kind.isObjectiveC ())
767
913
HeaderContents += " #import" ;
768
914
else
769
915
HeaderContents += " #include" ;
770
- HeaderContents += " \" " ;
771
- HeaderContents += FIF.getFile ();
772
- HeaderContents += " \"\n " ;
773
916
774
- KnownInputFiles.emplace_back (FIF.getFile ());
917
+ StringRef FilePath = FIF.getFile ();
918
+ if (auto RelativeName = getRelativeIncludeName (CI, FilePath, &IsQuoted)) {
919
+ if (IsQuoted)
920
+ HeaderContents += " \" " ;
921
+ else
922
+ HeaderContents += " <" ;
923
+
924
+ HeaderContents += *RelativeName;
925
+
926
+ if (IsQuoted)
927
+ HeaderContents += " \"\n " ;
928
+ else
929
+ HeaderContents += " >\n " ;
930
+ KnownInputFiles.emplace_back (*RelativeName, IsQuoted);
931
+ } else {
932
+ HeaderContents += " \" " ;
933
+ HeaderContents += FilePath;
934
+ HeaderContents += " \"\n " ;
935
+ KnownInputFiles.emplace_back (FilePath, true );
936
+ }
775
937
}
776
938
939
+ if (CI.getHeaderSearchOpts ().Verbose )
940
+ CI.getVerboseOutputStream () << getInputBufferName () << " :\n "
941
+ << HeaderContents << " \n " ;
942
+
777
943
Buffer = llvm::MemoryBuffer::getMemBufferCopy (HeaderContents,
778
944
getInputBufferName ());
779
945
0 commit comments