From: Douglas Gregor Date: Wed, 4 May 2011 00:14:37 +0000 (+0000) Subject: Introduce a new libclang API, clang_isFileMultipleIncludeGuarded(), X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=dd3e5549e3c11e217078938aacf72f042eea5343;p=clang Introduce a new libclang API, clang_isFileMultipleIncludeGuarded(), which determines whether a particular file is actually a header that is intended to be guarded from multiple inclusions within the same translation unit. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130808 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index d89a903e41..2f2876e0fa 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -221,6 +221,14 @@ CINDEX_LINKAGE CXString clang_getFileName(CXFile SFile); */ CINDEX_LINKAGE time_t clang_getFileTime(CXFile SFile); +/** + * \brief Determine whether the given header is guarded against + * multiple inclusions, either with the conventional + * #ifndef/#define/#endif macro guards or with #pragma once. + */ +CINDEX_LINKAGE unsigned +clang_isFileMultipleIncludeGuarded(CXTranslationUnit tu, CXFile file); + /** * \brief Retrieve a file handle within the given translation unit. * diff --git a/include/clang/Lex/HeaderSearch.h b/include/clang/Lex/HeaderSearch.h index fec4dad1e7..3d99f15412 100644 --- a/include/clang/Lex/HeaderSearch.h +++ b/include/clang/Lex/HeaderSearch.h @@ -31,6 +31,9 @@ struct HeaderFileInfo { /// isImport - True if this is a #import'd or #pragma once file. unsigned isImport : 1; + /// isPragmaOnce - True if this is #pragma once file. + unsigned isPragmaOnce : 1; + /// DirInfo - Keep track of whether this is a system header, and if so, /// whether it is C++ clean or not. This can be set by the include paths or /// by #pragma gcc system_header. This is an instance of @@ -66,8 +69,8 @@ struct HeaderFileInfo { const IdentifierInfo *ControllingMacro; HeaderFileInfo() - : isImport(false), DirInfo(SrcMgr::C_User), External(false), - Resolved(false), NumIncludes(0), ControllingMacroID(0), + : isImport(false), isPragmaOnce(false), DirInfo(SrcMgr::C_User), + External(false), Resolved(false), NumIncludes(0), ControllingMacroID(0), ControllingMacro(0) {} /// \brief Retrieve the controlling macro for this header file, if @@ -77,7 +80,8 @@ struct HeaderFileInfo { /// \brief Determine whether this is a non-default header file info, e.g., /// it corresponds to an actual header we've included or tried to include. bool isNonDefault() const { - return isImport || NumIncludes || ControllingMacro || ControllingMacroID; + return isImport || isPragmaOnce || NumIncludes || ControllingMacro || + ControllingMacroID; } }; @@ -242,7 +246,9 @@ public: /// MarkFileIncludeOnce - Mark the specified file as a "once only" file, e.g. /// due to #pragma once. void MarkFileIncludeOnce(const FileEntry *File) { - getFileInfo(File).isImport = true; + HeaderFileInfo &FI = getFileInfo(File); + FI.isImport = true; + FI.isPragmaOnce = true; } /// MarkFileSystemHeader - Mark the specified file as a system header, e.g. @@ -265,6 +271,13 @@ public: getFileInfo(File).ControllingMacro = ControllingMacro; } + /// \brief Determine whether this file is intended to be safe from + /// multiple inclusions, e.g., it has #pragma once or a controlling + /// macro. + /// + /// This routine does not consider the effect of #import + bool isFileMultipleIncludeGuarded(const FileEntry *File); + /// CreateHeaderMap - This method returns a HeaderMap for the specified /// FileEntry, uniquing them through the the 'HeaderMaps' datastructure. const HeaderMap *CreateHeaderMap(const FileEntry *FE); diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp index 372078c60d..b3301164bd 100644 --- a/lib/Lex/HeaderSearch.cpp +++ b/lib/Lex/HeaderSearch.cpp @@ -482,6 +482,21 @@ HeaderFileInfo &HeaderSearch::getFileInfo(const FileEntry *FE) { return HFI; } +bool HeaderSearch::isFileMultipleIncludeGuarded(const FileEntry *File) { + // Check if we've ever seen this file as a header. + if (File->getUID() >= FileInfo.size()) + return false; + + // Resolve header file info from the external source, if needed. + HeaderFileInfo &HFI = FileInfo[File->getUID()]; + if (ExternalSource && !HFI.Resolved) { + HFI = ExternalSource->GetHeaderFileInfo(File); + HFI.Resolved = true; + } + + return HFI.isPragmaOnce || HFI.ControllingMacro || HFI.ControllingMacroID; +} + void HeaderSearch::setHeaderFileInfoForUID(HeaderFileInfo HFI, unsigned UID) { if (UID >= FileInfo.size()) FileInfo.resize(UID+1); diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index d2e41a96c9..5a4c59e3cd 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -1697,7 +1697,8 @@ namespace { using namespace clang::io; HeaderFileInfo HFI; unsigned Flags = *d++; - HFI.isImport = (Flags >> 3) & 0x01; + HFI.isImport = (Flags >> 4) & 0x01; + HFI.isPragmaOnce = (Flags >> 3) & 0x01; HFI.DirInfo = (Flags >> 1) & 0x03; HFI.Resolved = Flags & 0x01; HFI.NumIncludes = ReadUnalignedLE16(d); diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 6d44fb63e5..e711351add 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1280,7 +1280,8 @@ namespace { using namespace clang::io; uint64_t Start = Out.tell(); (void)Start; - unsigned char Flags = (Data.isImport << 3) + unsigned char Flags = (Data.isImport << 4) + | (Data.isPragmaOnce << 3) | (Data.DirInfo << 1) | Data.Resolved; Emit8(Out, (uint8_t)Flags); diff --git a/test/Index/Inputs/guarded.h b/test/Index/Inputs/guarded.h new file mode 100644 index 0000000000..0a1a6ac5c2 --- /dev/null +++ b/test/Index/Inputs/guarded.h @@ -0,0 +1,6 @@ +#ifndef GUARDED_HEADER_H +#define GUARDED_HEADER_H + +int y; + +#endif // GUARDED_HEADER_H diff --git a/test/Index/Inputs/pragma-once.h b/test/Index/Inputs/pragma-once.h new file mode 100644 index 0000000000..503cb3f319 --- /dev/null +++ b/test/Index/Inputs/pragma-once.h @@ -0,0 +1,3 @@ +#pragma once +int i; + diff --git a/test/Index/annotate-tokens-pp.c b/test/Index/annotate-tokens-pp.c index 3dd9ffeddc..01a615f0e4 100644 --- a/test/Index/annotate-tokens-pp.c +++ b/test/Index/annotate-tokens-pp.c @@ -25,7 +25,10 @@ void test() { fun_with_macro_bodies(x, { int z = x; ++z; }); } -// RUN: c-index-test -test-annotate-tokens=%s:2:1:26:1 -I%S/Inputs %s | FileCheck %s +#include "pragma-once.h" +#include "guarded.h" + +// RUN: c-index-test -test-annotate-tokens=%s:2:1:30:1 -I%S/Inputs %s | FileCheck %s // CHECK: Punctuation: "#" [2:1 - 2:2] preprocessing directive= // CHECK: Identifier: "define" [2:2 - 2:8] preprocessing directive= // CHECK: Identifier: "STILL_NOTHING" [2:9 - 2:22] macro definition=STILL_NOTHING @@ -184,4 +187,5 @@ void test() { // CHECK: Punctuation: ")" [25:47 - 25:48] UnexposedStmt= // CHECK: Punctuation: ";" [25:48 - 25:49] UnexposedStmt= // CHECK: Punctuation: "}" [26:1 - 26:2] UnexposedStmt= - +// CHECK: {{28:1.*inclusion directive=pragma-once.h.*multi-include guarded}} +// CHECK: {{29:1.*inclusion directive=guarded.h.*multi-include guarded}} diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index f7b7a367cf..e43ac0699f 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -158,7 +158,7 @@ int parse_remapped_files(int argc, const char **argv, int start_arg, int want_display_name = 0; -static void PrintCursor(CXCursor Cursor) { +static void PrintCursor(CXTranslationUnit TU, CXCursor Cursor) { if (clang_isInvalid(Cursor.kind)) { CXString ks = clang_getCursorKindSpelling(Cursor.kind); printf("Invalid Cursor => %s", clang_getCString(ks)); @@ -277,6 +277,9 @@ static void PrintCursor(CXCursor Cursor) { CXString Included = clang_getFileName(File); printf(" (%s)", clang_getCString(Included)); clang_disposeString(Included); + + if (clang_isFileMultipleIncludeGuarded(TU, File)) + printf(" [multi-include guarded]"); } } } @@ -426,7 +429,7 @@ enum CXChildVisitResult FilteredPrintingVisitor(CXCursor Cursor, clang_getSpellingLocation(Loc, 0, &line, &column, 0); printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Cursor), line, column); - PrintCursor(Cursor); + PrintCursor(Data->TU, Cursor); PrintCursorExtent(Cursor); printf("\n"); return CXChildVisit_Recurse; @@ -479,7 +482,7 @@ static enum CXChildVisitResult FunctionScanVisitor(CXCursor Cursor, } else if (Ref.kind != CXCursor_FunctionDecl) { printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Ref), curLine, curColumn); - PrintCursor(Ref); + PrintCursor(Data->TU, Ref); printf("\n"); } } @@ -554,6 +557,8 @@ static enum CXChildVisitResult PrintLinkage(CXCursor cursor, CXCursor p, CXClientData d) { const char *linkage = 0; + VisitorData *Data = (VisitorData *)d; + if (clang_isInvalid(clang_getCursorKind(cursor))) return CXChildVisit_Recurse; @@ -566,7 +571,7 @@ static enum CXChildVisitResult PrintLinkage(CXCursor cursor, CXCursor p, } if (linkage) { - PrintCursor(cursor); + PrintCursor(Data->TU, cursor); printf("linkage=%s\n", linkage); } @@ -579,11 +584,12 @@ static enum CXChildVisitResult PrintLinkage(CXCursor cursor, CXCursor p, static enum CXChildVisitResult PrintTypeKind(CXCursor cursor, CXCursor p, CXClientData d) { + VisitorData *Data = (VisitorData *)d; if (!clang_isInvalid(clang_getCursorKind(cursor))) { CXType T = clang_getCursorType(cursor); CXString S = clang_getTypeKindSpelling(T.kind); - PrintCursor(cursor); + PrintCursor(Data->TU, cursor); printf(" typekind=%s", clang_getCString(S)); if (clang_isConstQualifiedType(T)) printf(" const"); @@ -778,7 +784,7 @@ int perform_test_reparse_source(int argc, const char **argv, int trials, /* Logic for testing clang_getCursor(). */ /******************************************************************************/ -static void print_cursor_file_scan(CXCursor cursor, +static void print_cursor_file_scan(CXTranslationUnit TU, CXCursor cursor, unsigned start_line, unsigned start_col, unsigned end_line, unsigned end_col, const char *prefix) { @@ -787,7 +793,7 @@ static void print_cursor_file_scan(CXCursor cursor, printf("-%s", prefix); PrintExtent(stdout, start_line, start_col, end_line, end_col); printf(" "); - PrintCursor(cursor); + PrintCursor(TU, cursor); printf("\n"); } @@ -832,7 +838,7 @@ static int perform_file_scan(const char *ast_file, const char *source_file, cursor = clang_getCursor(TU, clang_getLocation(TU, file, line, col)); if ((c == EOF || !clang_equalCursors(cursor, prevCursor)) && prevCursor.kind != CXCursor_InvalidFile) { - print_cursor_file_scan(prevCursor, start_line, start_col, + print_cursor_file_scan(TU, prevCursor, start_line, start_col, line, col, prefix); start_line = line; start_col = col; @@ -1183,7 +1189,7 @@ int inspect_cursor_at(int argc, const char **argv) { clang_getLocation(TU, file, Locations[Loc].line, Locations[Loc].column)); if (I + 1 == Repeats) { - PrintCursor(Cursor); + PrintCursor(TU, Cursor); printf("\n"); free(Locations[Loc].filename); } @@ -1287,7 +1293,7 @@ int perform_token_annotation(int argc, const char **argv) { PrintExtent(stdout, start_line, start_column, end_line, end_column); if (!clang_isInvalid(cursors[i].kind)) { printf(" "); - PrintCursor(cursors[i]); + PrintCursor(TU, cursors[i]); } printf("\n"); } diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 28f1506988..e6774dc895 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -30,6 +30,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Lex/Lexer.h" +#include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PreprocessingRecord.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/STLExtras.h" @@ -2890,6 +2891,16 @@ CXFile clang_getFile(CXTranslationUnit tu, const char *file_name) { return const_cast(FMgr.getFile(file_name)); } +unsigned clang_isFileMultipleIncludeGuarded(CXTranslationUnit tu, CXFile file) { + if (!tu || !file) + return 0; + + ASTUnit *CXXUnit = static_cast(tu->TUData); + FileEntry *FEnt = static_cast(file); + return CXXUnit->getPreprocessor().getHeaderSearchInfo() + .isFileMultipleIncludeGuarded(FEnt); +} + } // end: extern "C" //===----------------------------------------------------------------------===// diff --git a/tools/libclang/libclang.darwin.exports b/tools/libclang/libclang.darwin.exports index 30c3fd1ac3..7bba6739c5 100644 --- a/tools/libclang/libclang.darwin.exports +++ b/tools/libclang/libclang.darwin.exports @@ -115,6 +115,7 @@ _clang_isConstQualifiedType _clang_isCursorDefinition _clang_isDeclaration _clang_isExpression +_clang_isFileMultipleIncludeGuarded _clang_isInvalid _clang_isPODType _clang_isPreprocessing diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index 4e96e8a0d6..5f2c362f5b 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -115,6 +115,7 @@ clang_isConstQualifiedType clang_isCursorDefinition clang_isDeclaration clang_isExpression +clang_isFileMultipleIncludeGuarded clang_isInvalid clang_isPODType clang_isPreprocessing