| //===--- iwyu_location_util.h - SourceLoc-related utilities for iwyu ------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // clang-File utilities for the IWYU checker. |
| // |
| // This file deals with clang's FileEntry and SourceLocation classes, |
| // and useful utilities on them. It does not deal with file paths |
| // per se; for that, see iwyu_path_util.h |
| // |
| // In addition to some ad hoc helper routines, in general, for every |
| // type we consider, we try to provide 3 routines (as appropriate): |
| // GetLocation(): convert the object to an appropriate SourceLocation |
| // GetFileEntry(): convert the object to the file it's in |
| // GetFilePath(): convert the object to the filename it's in |
| // |
| // |
| // Background on Clang data structures: |
| // |
| // Clang uses the type FileEntry to identify a physical file in the |
| // file system. A FileEntry is created for each source file Clang |
| // processes. Clang never creates two FileEntry objects for the same |
| // file. Therefore we use const FileEntry* in IWYU as unique IDs for |
| // files. |
| // |
| // Clang's FileID type is a misnomer. It's actually an ID of a |
| // particular #include statement. If a file is #included in two |
| // places, it will have two different FildIDs. |
| // |
| // A SourceLocation is a compact encoding of a location in a |
| // particular #include of a source file. From it you can find the |
| // file path, line number, column number, and which file (if any) |
| // #includes the file. |
| |
| #ifndef INCLUDE_WHAT_YOU_USE_IWYU_LOCATION_UTIL_H_ |
| #define INCLUDE_WHAT_YOU_USE_IWYU_LOCATION_UTIL_H_ |
| |
| #include <string> // for string |
| |
| #include "iwyu_globals.h" |
| #include "iwyu_path_util.h" |
| #include "clang/Basic/FileManager.h" |
| #include "clang/Basic/SourceLocation.h" |
| #include "clang/Basic/SourceManager.h" |
| #include "clang/Lex/Token.h" |
| |
| namespace clang { |
| class Decl; |
| class NestedNameSpecifierLoc; |
| class Stmt; |
| class TemplateArgumentLoc; |
| class TypeLoc; |
| } // namespace clang |
| |
| namespace include_what_you_use { |
| |
| using std::string; |
| |
| //------------------------------------------------------------ |
| // Helper functions for FileEntry. |
| |
| // Some symbols are directly defined by the compiler. For them, the |
| // definition location points to the "<built-in>" file. |
| inline bool IsBuiltinFile(const clang::FileEntry* file) { |
| return file == nullptr; |
| } |
| |
| // There are two kinds of symbols that are not defined in the source |
| // files: the compiler can define some standard symbols |
| // (e.g. __FILE__), and the user can define macros on the command line |
| // of the compiler using -D. A symbol appears to be defined in file |
| // "<built-in>" in the first case, and "<command line>" in the second. |
| // IsBuiltinOrCommandLineFile(file) returns true if it's either of the |
| // two cases. |
| inline bool IsBuiltinOrCommandLineFile(const clang::FileEntry* file) { |
| return IsBuiltinFile(file) || file->getName().equals("<command line>"); |
| } |
| |
| // When macro args are concatenated e.g. '#define CAT(A, B) A##B', their |
| // location ends up outside the source text, in what the compiler calls |
| // "<scratch space>". |
| // This function returns true if the provided loc is in scratch space. |
| bool IsInScratchSpace(clang::SourceLocation loc); |
| |
| inline string GetFilePath(const clang::FileEntry* file) { |
| return (IsBuiltinFile(file) ? "<built-in>" : |
| NormalizeFilePath(file->getName().str())); |
| } |
| |
| //------------------------------------------------------------ |
| // Helper functions for SourceLocation |
| |
| inline clang::SourceLocation GetSpellingLoc(clang::SourceLocation loc) { |
| return loc.isValid() ? GlobalSourceManager()->getSpellingLoc(loc) : loc; |
| } |
| |
| inline clang::SourceLocation GetInstantiationLoc(clang::SourceLocation loc) { |
| return loc.isValid() ? GlobalSourceManager()->getExpansionLoc(loc) : loc; |
| } |
| |
| inline bool IsInMacro(clang::SourceLocation loc) { |
| return GetSpellingLoc(loc) != GetInstantiationLoc(loc); |
| } |
| |
| // Returns the line number corresponding to a source location. |
| // Prefers the spelling line number, but if it's not useful (because |
| // the location is in <scratch space> for instance, due to macro token |
| // concatenation), uses the instantiation line number. Returns -1 if |
| // we can't figure out the line #. |
| inline int GetLineNumber(clang::SourceLocation loc) { |
| if (!loc.isValid()) |
| return -1; |
| const clang::FullSourceLoc fullloc(loc, *GlobalSourceManager()); |
| bool invalid = false; |
| int retval = fullloc.getSpellingLineNumber(&invalid); |
| if (invalid) |
| retval = fullloc.getExpansionLineNumber(&invalid); |
| if (invalid) |
| retval = -1; |
| return retval; |
| } |
| |
| // The rest of this section of the file is for returning the |
| // FileEntry* corresponding to a source location: the file that the |
| // location is in. This is a surprising amount of work. |
| |
| // Tells which #include loc comes from. |
| // This is the most basic FileEntry getter, it only does a simple lookup in |
| // SourceManager to determine which file the location is associated with. |
| inline const clang::FileEntry* GetLocFileEntry(clang::SourceLocation loc) { |
| // clang uses the name FileID to mean 'a filename that was reached via |
| // a particular series of #includes.' (What one might think a FileID |
| // might be -- a unique reference to a filesystem object -- is |
| // actually a FileEntry*.) |
| const clang::SourceManager& source_manager = *GlobalSourceManager(); |
| return source_manager.getFileEntryForID(source_manager.getFileID(loc)); |
| } |
| |
| inline const clang::FileEntry* GetFileEntry(clang::SourceLocation loc) { |
| if (!loc.isValid()) |
| return nullptr; |
| |
| // We want where the user actually writes the token, instead of |
| // where it appears as part of a macro expansion. For example, in: |
| // |
| // file foo.h, line 5: #define FOO(x) x + y |
| // file bar.cc, line 10: FOO(z) |
| // |
| // FOO(z) will expand to 'z + y', where symbol z's location is |
| // foo.h, line 5, and its spelling location is bar.cc, line 10. |
| const clang::FileEntry* retval = GetLocFileEntry(GetSpellingLoc(loc)); |
| |
| // Sometimes the spelling location is NULL, because the symbol is |
| // 'spelled' via macro concatenation. For instance, all the |
| // __gthrw3 symbols in |
| // /usr/include/c++/4.2/x86_64-linux-gnu/bits/gthr-default.h. |
| // In that case, fall back on the instantiation location. |
| if (!retval) { |
| retval = GetLocFileEntry(GetInstantiationLoc(loc)); |
| } |
| return retval; |
| } |
| |
| //------------------------------------------------------------ |
| // GetFileEntry(), GetFilePath(), and GetLocation(). |
| |
| inline clang::SourceLocation GetLocation(const clang::Token& token) { |
| return token.getLocation(); |
| } |
| |
| inline clang::SourceLocation GetLocation(clang::SourceLocation loc) { |
| return loc; // the identity location-getter, useful with templates |
| } |
| clang::SourceLocation GetLocation(const clang::Decl* decl); |
| clang::SourceLocation GetLocation(const clang::Stmt* stmt); |
| clang::SourceLocation GetLocation(const clang::TypeLoc* typeloc); |
| clang::SourceLocation GetLocation(const clang::NestedNameSpecifierLoc* nnsloc); |
| clang::SourceLocation GetLocation(const clang::TemplateArgumentLoc* argloc); |
| |
| // These define default implementations of GetFileEntry() and |
| // GetPath() in terms of GetLocation(). As long as an object defines |
| // its own GetLocation(), it will get these other two for free. |
| template<typename T> const clang::FileEntry* GetFileEntry(const T& obj) { |
| return GetFileEntry(GetLocation(obj)); |
| } |
| template<typename T> const string GetFilePath(const T& obj) { |
| return GetFilePath(GetFileEntry(obj)); |
| } |
| |
| //------------------------------------------------------------ |
| // Some utility, location-based routines. |
| |
| // Given any two objects that have instantiation-locations, says |
| // whether one occurs before the other in the translation unit (using |
| // instantiated locations). This means that one would occur before |
| // the other looking at the output of cc -E or equivalent. |
| template<typename T, typename U> |
| inline bool IsBeforeInTranslationUnit(const T& a, const U& b) { |
| const clang::FullSourceLoc a_loc(GetLocation(a), *GlobalSourceManager()); |
| const clang::FullSourceLoc b_loc(GetLocation(b), *GlobalSourceManager()); |
| // Inside a macro, everything has the same instantiation location. |
| // We'd like to use spelling-location to break that tie, but it's |
| // unreliable since a or b might be spelled in "<scratch space>". |
| // So we're just conservative and return true always if the two have |
| // an equal location and are in a macro. (Because we check the |
| // instantiation-location is equal, it's enough that one of the two |
| // be in a macro; we prefer that since IsInMacro fails if T or U is |
| // the wrong type.) TODO(csilvers): see if's possible to get |
| // isBeforeInTranslationUnitThan working properly. This may require |
| // storing source-locations better in OneUse. |
| if ((IsInMacro(a_loc) || IsInMacro(b_loc)) && |
| GetInstantiationLoc(a_loc) == GetInstantiationLoc(b_loc)) |
| return true; |
| return a_loc.isBeforeInTranslationUnitThan(b_loc); |
| } |
| |
| // Like IsBeforeInTranslationUnit, but both a and b must be |
| // instantiated in the same file as well. |
| template<typename T, typename U> |
| inline bool IsBeforeInSameFile(const T& a, const U& b) { |
| if (GetFileEntry(a) != GetFileEntry(b)) |
| return false; |
| return IsBeforeInTranslationUnit(a, b); |
| } |
| |
| } // namespace include_what_you_use |
| |
| #endif // INCLUDE_WHAT_YOU_USE_IWYU_LOCATION_UTIL_H_ |