From 75d7d8f2b2eb6b2e69a5d045f08d70897729ac6d Mon Sep 17 00:00:00 2001 From: Will Wilson Date: Fri, 27 Dec 2013 19:46:16 +0000 Subject: [PATCH] Implement MSVC header search algorithm in MicrosoftMode. Follows algorithm described here: http://msdn.microsoft.com/en-us/library/36k2cdd4.aspx git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@198082 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticLexKinds.td | 3 + include/clang/Lex/HeaderSearch.h | 14 +- lib/Lex/HeaderSearch.cpp | 122 +++++++++--------- lib/Lex/PPDirectives.cpp | 33 +++-- lib/Rewrite/Frontend/InclusionRewriter.cpp | 2 +- test/Preprocessor/microsoft-header-search.c | 6 + .../microsoft-header-search/a/b/include3.h | 3 + .../microsoft-header-search/a/findme.h | 3 + .../microsoft-header-search/a/include2.h | 6 + .../microsoft-header-search/findme.h | 3 + .../microsoft-header-search/include1.h | 6 + 11 files changed, 127 insertions(+), 74 deletions(-) create mode 100644 test/Preprocessor/microsoft-header-search.c create mode 100644 test/Preprocessor/microsoft-header-search/a/b/include3.h create mode 100644 test/Preprocessor/microsoft-header-search/a/findme.h create mode 100644 test/Preprocessor/microsoft-header-search/a/include2.h create mode 100644 test/Preprocessor/microsoft-header-search/findme.h create mode 100644 test/Preprocessor/microsoft-header-search/include1.h diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index e59ded86e7..136ac705c2 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -300,6 +300,9 @@ def ext_pp_import_directive : Extension<"#import is a language extension">, InGroup>; def err_pp_import_directive_ms : Error< "#import of type library is an unsupported Microsoft feature">; +def ext_pp_include_search_ms : ExtWarn< + "#include resolved using non-portable MSVC search rules as: %0">, + InGroup>; def ext_pp_ident_directive : Extension<"#ident is a language extension">; def ext_pp_include_next_directive : Extension< diff --git a/include/clang/Lex/HeaderSearch.h b/include/clang/Lex/HeaderSearch.h index 283bd5714f..dec4d53813 100644 --- a/include/clang/Lex/HeaderSearch.h +++ b/include/clang/Lex/HeaderSearch.h @@ -158,6 +158,8 @@ class HeaderSearch { /// \brief Header-search options used to initialize this header search. IntrusiveRefCntPtr HSOpts; + DiagnosticsEngine &Diags; + SourceManager &SourceMgr; FileManager &FileMgr; /// \#include search path information. Requests for \#include "x" search the /// directory of the \#including file first, then each directory in SearchDirs @@ -349,13 +351,15 @@ public: /// \returns If successful, this returns 'UsedDir', the DirectoryLookup member /// the file was found in, or null if not applicable. /// + /// \param IncludeLoc Used for diagnostics if valid. + /// /// \param isAngled indicates whether the file reference is a <> reference. /// /// \param CurDir If non-null, the file was found in the specified directory /// search location. This is used to implement \#include_next. /// - /// \param CurFileEnt If non-null, indicates where the \#including file is, in - /// case a relative search is needed. + /// \param Includers Indicates where the \#including file(s) are, in case + /// relative searches are needed. In reverse order of inclusion. /// /// \param SearchPath If non-null, will be set to the search path relative /// to which the file was found. If the include path is absolute, SearchPath @@ -368,10 +372,10 @@ public: /// \param SuggestedModule If non-null, and the file found is semantically /// part of a known module, this will be set to the module that should /// be imported instead of preprocessing/parsing the file found. - const FileEntry *LookupFile(StringRef Filename, bool isAngled, - const DirectoryLookup *FromDir, + const FileEntry *LookupFile(StringRef Filename, SourceLocation IncludeLoc, + bool isAngled, const DirectoryLookup *FromDir, const DirectoryLookup *&CurDir, - const FileEntry *CurFileEnt, + ArrayRef Includers, SmallVectorImpl *SearchPath, SmallVectorImpl *RelativePath, ModuleMap::KnownHeader *SuggestedModule, diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp index 95d74e5d72..4b447ba000 100644 --- a/lib/Lex/HeaderSearch.cpp +++ b/lib/Lex/HeaderSearch.cpp @@ -12,12 +12,12 @@ //===----------------------------------------------------------------------===// #include "clang/Lex/HeaderSearch.h" -#include "clang/Basic/Diagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Lex/HeaderMap.h" #include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/Lexer.h" +#include "clang/Lex/LexDiagnostic.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Capacity.h" #include "llvm/Support/FileSystem.h" @@ -45,11 +45,11 @@ ExternalHeaderFileInfoSource::~ExternalHeaderFileInfoSource() {} HeaderSearch::HeaderSearch(IntrusiveRefCntPtr HSOpts, SourceManager &SourceMgr, DiagnosticsEngine &Diags, - const LangOptions &LangOpts, + const LangOptions &LangOpts, const TargetInfo *Target) - : HSOpts(HSOpts), FileMgr(SourceMgr.getFileManager()), FrameworkMap(64), - ModMap(SourceMgr, Diags, LangOpts, Target, *this) -{ + : HSOpts(HSOpts), Diags(Diags), SourceMgr(SourceMgr), + FileMgr(SourceMgr.getFileManager()), FrameworkMap(64), + ModMap(SourceMgr, Diags, LangOpts, Target, *this) { AngledDirIdx = 0; SystemDirIdx = 0; NoCurDirSearch = false; @@ -493,20 +493,15 @@ void HeaderSearch::setTarget(const TargetInfo &Target) { /// LookupFile - Given a "foo" or \ reference, look up the indicated file, /// return null on failure. isAngled indicates whether the file reference is -/// for system \#include's or not (i.e. using <> instead of ""). CurFileEnt, if -/// non-null, indicates where the \#including file is, in case a relative search -/// is needed. +/// for system \#include's or not (i.e. using <> instead of ""). Includers, if +/// non-empty, indicates where the \#including file(s) are, in case a relative +/// search is needed. Microsoft mode will pass all \#including files. const FileEntry *HeaderSearch::LookupFile( - StringRef Filename, - bool isAngled, - const DirectoryLookup *FromDir, - const DirectoryLookup *&CurDir, - const FileEntry *CurFileEnt, - SmallVectorImpl *SearchPath, + StringRef Filename, SourceLocation IncludeLoc, bool isAngled, + const DirectoryLookup *FromDir, const DirectoryLookup *&CurDir, + ArrayRef Includers, SmallVectorImpl *SearchPath, SmallVectorImpl *RelativePath, - ModuleMap::KnownHeader *SuggestedModule, - bool SkipCache) -{ + ModuleMap::KnownHeader *SuggestedModule, bool SkipCache) { if (!HSOpts->ModuleMapFiles.empty()) { // Preload all explicitly specified module map files. This enables modules // map files lying in a directory structure separate from the header files @@ -546,44 +541,53 @@ const FileEntry *HeaderSearch::LookupFile( } // Unless disabled, check to see if the file is in the #includer's - // directory. This has to be based on CurFileEnt, not CurDir, because - // CurFileEnt could be a #include of a subdirectory (#include "foo/bar.h") and - // a subsequent include of "baz.h" should resolve to "whatever/foo/baz.h". + // directory. This cannot be based on CurDir, because each includer could be + // a #include of a subdirectory (#include "foo/bar.h") and a subsequent + // include of "baz.h" should resolve to "whatever/foo/baz.h". // This search is not done for <> headers. - if (CurFileEnt && !isAngled && !NoCurDirSearch) { + if (!Includers.empty() && !isAngled && !NoCurDirSearch) { SmallString<1024> TmpDir; - // Concatenate the requested file onto the directory. - // FIXME: Portability. Filename concatenation should be in sys::Path. - TmpDir += CurFileEnt->getDir()->getName(); - TmpDir.push_back('/'); - TmpDir.append(Filename.begin(), Filename.end()); - if (const FileEntry *FE = FileMgr.getFile(TmpDir.str(),/*openFile=*/true)) { - // Leave CurDir unset. - // This file is a system header or C++ unfriendly if the old file is. - // - // Note that we only use one of FromHFI/ToHFI at once, due to potential - // reallocation of the underlying vector potentially making the first - // reference binding dangling. - HeaderFileInfo &FromHFI = getFileInfo(CurFileEnt); - unsigned DirInfo = FromHFI.DirInfo; - bool IndexHeaderMapHeader = FromHFI.IndexHeaderMapHeader; - StringRef Framework = FromHFI.Framework; - - HeaderFileInfo &ToHFI = getFileInfo(FE); - ToHFI.DirInfo = DirInfo; - ToHFI.IndexHeaderMapHeader = IndexHeaderMapHeader; - ToHFI.Framework = Framework; - - if (SearchPath != NULL) { - StringRef SearchPathRef(CurFileEnt->getDir()->getName()); - SearchPath->clear(); - SearchPath->append(SearchPathRef.begin(), SearchPathRef.end()); - } - if (RelativePath != NULL) { - RelativePath->clear(); - RelativePath->append(Filename.begin(), Filename.end()); + for (ArrayRef::iterator I(Includers.begin()), + E(Includers.end()); + I != E; ++I) { + const FileEntry *Includer = *I; + // Concatenate the requested file onto the directory. + // FIXME: Portability. Filename concatenation should be in sys::Path. + TmpDir = Includer->getDir()->getName(); + TmpDir.push_back('/'); + TmpDir.append(Filename.begin(), Filename.end()); + if (const FileEntry *FE = + FileMgr.getFile(TmpDir.str(), /*openFile=*/true)) { + // Leave CurDir unset. + // This file is a system header or C++ unfriendly if the old file is. + // + // Note that we only use one of FromHFI/ToHFI at once, due to potential + // reallocation of the underlying vector potentially making the first + // reference binding dangling. + HeaderFileInfo &FromHFI = getFileInfo(Includer); + unsigned DirInfo = FromHFI.DirInfo; + bool IndexHeaderMapHeader = FromHFI.IndexHeaderMapHeader; + StringRef Framework = FromHFI.Framework; + + HeaderFileInfo &ToHFI = getFileInfo(FE); + ToHFI.DirInfo = DirInfo; + ToHFI.IndexHeaderMapHeader = IndexHeaderMapHeader; + ToHFI.Framework = Framework; + + if (SearchPath != NULL) { + StringRef SearchPathRef(Includer->getDir()->getName()); + SearchPath->clear(); + SearchPath->append(SearchPathRef.begin(), SearchPathRef.end()); + } + if (RelativePath != NULL) { + RelativePath->clear(); + RelativePath->append(Filename.begin(), Filename.end()); + } + if (I != Includers.begin()) + Diags.Report(IncludeLoc, diag::ext_pp_include_search_ms) + << FE->getName(); + return FE; } - return FE; } } @@ -667,18 +671,18 @@ const FileEntry *HeaderSearch::LookupFile( // a header in a framework that is currently being built, and we couldn't // resolve "foo.h" any other way, change the include to , where // "Foo" is the name of the framework in which the including header was found. - if (CurFileEnt && !isAngled && Filename.find('/') == StringRef::npos) { - HeaderFileInfo &IncludingHFI = getFileInfo(CurFileEnt); + if (!Includers.empty() && !isAngled && + Filename.find('/') == StringRef::npos) { + HeaderFileInfo &IncludingHFI = getFileInfo(Includers.front()); if (IncludingHFI.IndexHeaderMapHeader) { SmallString<128> ScratchFilename; ScratchFilename += IncludingHFI.Framework; ScratchFilename += '/'; ScratchFilename += Filename; - - const FileEntry *Result = LookupFile(ScratchFilename, /*isAngled=*/true, - FromDir, CurDir, CurFileEnt, - SearchPath, RelativePath, - SuggestedModule); + + const FileEntry *Result = LookupFile( + ScratchFilename, IncludeLoc, /*isAngled=*/true, FromDir, CurDir, + Includers.front(), SearchPath, RelativePath, SuggestedModule); std::pair &CacheLookup = LookupFileCache.GetOrCreateValue(Filename).getValue(); CacheLookup.second diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index b93a39188a..80683f3f75 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -560,12 +560,12 @@ const FileEntry *Preprocessor::LookupFile( SmallVectorImpl *RelativePath, ModuleMap::KnownHeader *SuggestedModule, bool SkipCache) { - // If the header lookup mechanism may be relative to the current file, pass in - // info about where the current file is. - const FileEntry *CurFileEnt = 0; + // If the header lookup mechanism may be relative to the current inclusion + // stack, record the parent #includes. + SmallVector Includers; if (!FromDir) { FileID FID = getCurrentFileLexer()->getFileID(); - CurFileEnt = SourceMgr.getFileEntryForID(FID); + const FileEntry *FileEnt = SourceMgr.getFileEntryForID(FID); // If there is no file entry associated with this file, it must be the // predefines buffer. Any other file is not lexed with a normal lexer, so @@ -573,17 +573,31 @@ const FileEntry *Preprocessor::LookupFile( // predefines buffer, resolve #include references (which come from the // -include command line argument) as if they came from the main file, this // affects file lookup etc. - if (CurFileEnt == 0) { - FID = SourceMgr.getMainFileID(); - CurFileEnt = SourceMgr.getFileEntryForID(FID); + if (!FileEnt) + FileEnt = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); + + if (FileEnt) + Includers.push_back(FileEnt); + + // MSVC searches the current include stack from top to bottom for + // headers included by quoted include directives. + // See: http://msdn.microsoft.com/en-us/library/36k2cdd4.aspx + if (LangOpts.MicrosoftMode && !isAngled) { + for (unsigned i = 0, e = IncludeMacroStack.size(); i != e; ++i) { + IncludeStackInfo &ISEntry = IncludeMacroStack[e - i - 1]; + if (IsFileLexer(ISEntry)) + if ((FileEnt = SourceMgr.getFileEntryForID( + ISEntry.ThePPLexer->getFileID()))) + Includers.push_back(FileEnt); + } } } // Do a standard file entry lookup. CurDir = CurDirLookup; const FileEntry *FE = HeaderInfo.LookupFile( - Filename, isAngled, FromDir, CurDir, CurFileEnt, - SearchPath, RelativePath, SuggestedModule, SkipCache); + Filename, FilenameLoc, isAngled, FromDir, CurDir, Includers, SearchPath, + RelativePath, SuggestedModule, SkipCache); if (FE) { if (SuggestedModule) HeaderInfo.getModuleMap().diagnoseHeaderInclusion( @@ -591,6 +605,7 @@ const FileEntry *Preprocessor::LookupFile( return FE; } + const FileEntry *CurFileEnt; // Otherwise, see if this is a subframework header. If so, this is relative // to one of the headers on the #include stack. Walk the list of the current // headers on the #include stack and pass them to HeaderInfo. diff --git a/lib/Rewrite/Frontend/InclusionRewriter.cpp b/lib/Rewrite/Frontend/InclusionRewriter.cpp index 0e3e53498b..f304c94164 100644 --- a/lib/Rewrite/Frontend/InclusionRewriter.cpp +++ b/lib/Rewrite/Frontend/InclusionRewriter.cpp @@ -335,7 +335,7 @@ bool InclusionRewriter::HandleHasInclude( bool isAngled = PP.GetIncludeFilenameSpelling(Tok.getLocation(), Filename); const DirectoryLookup *CurDir; const FileEntry *File = PP.getHeaderSearchInfo().LookupFile( - Filename, isAngled, 0, CurDir, + Filename, SourceLocation(), isAngled, 0, CurDir, PP.getSourceManager().getFileEntryForID(FileId), 0, 0, 0, false); FileExists = File != 0; diff --git a/test/Preprocessor/microsoft-header-search.c b/test/Preprocessor/microsoft-header-search.c new file mode 100644 index 0000000000..6c39b968ef --- /dev/null +++ b/test/Preprocessor/microsoft-header-search.c @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -I%S/microsoft-header-search %s -fms-compatibility -verify + +// expected-warning@microsoft-header-search/a/findme.h:3 {{findme.h successfully included using MS search rules}} +// expected-warning@microsoft-header-search/a/b/include3.h:3 {{#include resolved using non-portable MSVC search rules as}} + +#include "microsoft-header-search/include1.h" \ No newline at end of file diff --git a/test/Preprocessor/microsoft-header-search/a/b/include3.h b/test/Preprocessor/microsoft-header-search/a/b/include3.h new file mode 100644 index 0000000000..6f6ce4028f --- /dev/null +++ b/test/Preprocessor/microsoft-header-search/a/b/include3.h @@ -0,0 +1,3 @@ +#pragma once + +#include "findme.h" \ No newline at end of file diff --git a/test/Preprocessor/microsoft-header-search/a/findme.h b/test/Preprocessor/microsoft-header-search/a/findme.h new file mode 100644 index 0000000000..b809c9075d --- /dev/null +++ b/test/Preprocessor/microsoft-header-search/a/findme.h @@ -0,0 +1,3 @@ +#pragma once + +#warning findme.h successfully included using MS search rules \ No newline at end of file diff --git a/test/Preprocessor/microsoft-header-search/a/include2.h b/test/Preprocessor/microsoft-header-search/a/include2.h new file mode 100644 index 0000000000..99640ae47a --- /dev/null +++ b/test/Preprocessor/microsoft-header-search/a/include2.h @@ -0,0 +1,6 @@ +#pragma once + +#include "b/include3.h" +#pragma once + +#include "b/include3.h" \ No newline at end of file diff --git a/test/Preprocessor/microsoft-header-search/findme.h b/test/Preprocessor/microsoft-header-search/findme.h new file mode 100644 index 0000000000..aeaf795a1a --- /dev/null +++ b/test/Preprocessor/microsoft-header-search/findme.h @@ -0,0 +1,3 @@ +#pragma once + +#error Wrong findme.h included, MSVC header search incorrect \ No newline at end of file diff --git a/test/Preprocessor/microsoft-header-search/include1.h b/test/Preprocessor/microsoft-header-search/include1.h new file mode 100644 index 0000000000..f00fac7a17 --- /dev/null +++ b/test/Preprocessor/microsoft-header-search/include1.h @@ -0,0 +1,6 @@ +#pragma once + +#include "a/include2.h" +#pragma once + +#include "a/include2.h" \ No newline at end of file -- 2.40.0