diff options
author | dim <dim@FreeBSD.org> | 2013-12-22 00:07:40 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2013-12-22 00:07:40 +0000 |
commit | 952eddef9aff85b1e92626e89baaf7a360e2ac85 (patch) | |
tree | df8df0b0067b381eab470a3b8f28d14a552a6340 /tools | |
parent | ea266cad53e3d49771fa38103913d3ec7a166694 (diff) | |
download | FreeBSD-src-952eddef9aff85b1e92626e89baaf7a360e2ac85.zip FreeBSD-src-952eddef9aff85b1e92626e89baaf7a360e2ac85.tar.gz |
Vendor import of clang release_34 branch r197841 (effectively, 3.4 RC3):
https://llvm.org/svn/llvm-project/cfe/branches/release_34@197841
Diffstat (limited to 'tools')
69 files changed, 2990 insertions, 2680 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index eb5e366..fef0adc 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,11 +1,18 @@ -add_subdirectory(libclang) -add_subdirectory(c-index-test) -add_subdirectory(arcmt-test) -add_subdirectory(c-arcmt-test) add_subdirectory(diagtool) add_subdirectory(driver) -add_subdirectory(clang-check) -add_subdirectory(clang-format) +if(CLANG_ENABLE_REWRITER) + add_subdirectory(clang-format) +endif() + +if(CLANG_ENABLE_ARCMT) + add_subdirectory(libclang) + add_subdirectory(c-index-test) + add_subdirectory(arcmt-test) + add_subdirectory(c-arcmt-test) +endif() +if(CLANG_ENABLE_STATIC_ANALYZER) + add_subdirectory(clang-check) +endif() # We support checking out the clang-tools-extra repository into the 'extra' # subdirectory. It contains tools developed as part of the Clang/LLVM project diff --git a/tools/Makefile b/tools/Makefile index b33c74d..94032d2 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -11,15 +11,29 @@ CLANG_LEVEL := .. include $(CLANG_LEVEL)/../../Makefile.config -DIRS := driver libclang c-index-test arcmt-test c-arcmt-test diagtool \ - clang-check clang-format +DIRS := +PARALLEL_DIRS := driver diagtool + +ifeq ($(ENABLE_CLANG_REWRITER),1) + PARALLEL_DIRS += clang-format +endif + +ifeq ($(ENABLE_CLANG_STATIC_ANALYZER), 1) + PARALLEL_DIRS += clang-check +endif + +ifeq ($(ENABLE_CLANG_ARCMT), 1) + DIRS += libclang c-index-test c-arcmt-test + PARALLEL_DIRS += arcmt-test +endif # Recurse into the extra repository of tools if present. -OPTIONAL_DIRS := extra +OPTIONAL_PARALLEL_DIRS := extra ifeq ($(BUILD_CLANG_ONLY),YES) - DIRS := driver libclang c-index-test - OPTIONAL_DIRS := + DIRS := libclang c-index-test + PARALLEL_DIRS := driver + OPTIONAL_PARALLEL_DIRS := endif include $(CLANG_LEVEL)/Makefile diff --git a/tools/arcmt-test/Makefile b/tools/arcmt-test/Makefile index 52898ce..4b9b8db 100644 --- a/tools/arcmt-test/Makefile +++ b/tools/arcmt-test/Makefile @@ -17,7 +17,7 @@ TOOL_NO_EXPORTS = 1 NO_INSTALL = 1 include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangARCMigrate.a clangRewriteCore.a \ clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \ clangSema.a clangEdit.a clangAnalysis.a clangAST.a clangLex.a \ diff --git a/tools/arcmt-test/arcmt-test.cpp b/tools/arcmt-test/arcmt-test.cpp index 179a115..50426e3 100644 --- a/tools/arcmt-test/arcmt-test.cpp +++ b/tools/arcmt-test/arcmt-test.cpp @@ -61,11 +61,11 @@ static llvm::cl::extrahelp extraHelp( // GetMainExecutable (since some platforms don't support taking the // address of main, and some platforms can't implement GetMainExecutable // without being given the address of a function in the main executable). -llvm::sys::Path GetExecutablePath(const char *Argv0) { +std::string GetExecutablePath(const char *Argv0) { // This just needs to be some symbol in the binary; C++ doesn't // allow taking the address of ::main however. void *MainAddr = (void*) (intptr_t) GetExecutablePath; - return llvm::sys::Path::GetMainExecutable(Argv0, MainAddr); + return llvm::sys::fs::getMainExecutable(Argv0, MainAddr); } static void printSourceLocation(SourceLocation loc, ASTContext &Ctx, diff --git a/tools/c-arcmt-test/Makefile b/tools/c-arcmt-test/Makefile index 02b8ab7..4a01c72 100644 --- a/tools/c-arcmt-test/Makefile +++ b/tools/c-arcmt-test/Makefile @@ -21,7 +21,7 @@ NO_INSTALL = 1 # LINK_COMPONENTS before including Makefile.rules include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option # Note that 'USEDLIBS' must include all of the core clang libraries # when -static is given to linker on cygming. @@ -29,7 +29,7 @@ USEDLIBS = clang.a \ clangARCMigrate.a \ clangRewriteFrontend.a \ clangRewriteCore.a \ - clangFrontend.a clangDriver.a \ + clangIndex.a clangFrontend.a clangDriver.a \ clangSerialization.a clangParse.a clangSema.a \ clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangBasic.a diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index d90dc6d..d850411 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -24,6 +24,6 @@ set_target_properties(c-index-test # If libxml2 is available, make it available for c-index-test. if (CLANG_HAVE_LIBXML) - include_directories(${LIBXML2_INCLUDE_DIR}) + include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) target_link_libraries(c-index-test ${LIBXML2_LIBRARIES}) endif() diff --git a/tools/c-index-test/Makefile b/tools/c-index-test/Makefile index 7723115..b38d654 100644 --- a/tools/c-index-test/Makefile +++ b/tools/c-index-test/Makefile @@ -22,12 +22,12 @@ TOOL_NO_EXPORTS = 1 # LINK_COMPONENTS before including Makefile.rules include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option # Note that 'USEDLIBS' must include all of the core clang libraries # when -static is given to linker on cygming. USEDLIBS = clang.a \ - clangFormat.a clangRewriteCore.a \ + clangIndex.a clangFormat.a clangRewriteCore.a \ clangFrontend.a clangDriver.a \ clangTooling.a \ clangSerialization.a clangParse.a clangSema.a \ @@ -37,4 +37,12 @@ USEDLIBS = clang.a \ include $(CLANG_LEVEL)/Makefile LIBS += $(LIBXML2_LIBS) + +# Headers in $(LIBXML2_INC) should not be checked with clang's -Wdocumentation. +# Use -isystem instead of -I then. +# FIXME: Could autoconf detect clang or availability of -isystem? +ifneq ($(findstring -Wdocumentation,$(OPTIMIZE_OPTION)),) +CPPFLAGS += $(subst -I,-isystem ,$(LIBXML2_INC)) +else CPPFLAGS += $(LIBXML2_INC) +endif diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index e575234..90a6528 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -694,10 +694,13 @@ static void PrintCursor(CXCursor Cursor, printf(" (static)"); if (clang_CXXMethod_isVirtual(Cursor)) printf(" (virtual)"); - + if (clang_CXXMethod_isPureVirtual(Cursor)) + printf(" (pure)"); if (clang_Cursor_isVariadic(Cursor)) printf(" (variadic)"); - + if (clang_Cursor_isObjCOptional(Cursor)) + printf(" (@optional)"); + if (Cursor.kind == CXCursor_IBOutletCollectionAttr) { CXType T = clang_getCanonicalType(clang_getIBOutletCollectionType(Cursor)); @@ -1157,6 +1160,7 @@ static enum CXChildVisitResult PrintType(CXCursor cursor, CXCursor p, CXClientData d) { if (!clang_isInvalid(clang_getCursorKind(cursor))) { CXType T = clang_getCursorType(cursor); + enum CXRefQualifierKind RQ = clang_Type_getCXXRefQualifier(T); PrintCursor(cursor, NULL); PrintTypeAndTypeKind(T, " [type=%s] [typekind=%s]"); if (clang_isConstQualifiedType(T)) @@ -1165,6 +1169,10 @@ static enum CXChildVisitResult PrintType(CXCursor cursor, CXCursor p, printf(" volatile"); if (clang_isRestrictQualifiedType(T)) printf(" restrict"); + if (RQ == CXRefQualifier_LValue) + printf(" lvalue-ref-qualifier"); + if (RQ == CXRefQualifier_RValue) + printf(" rvalue-ref-qualifier"); /* Print the canonical type if it is different. */ { CXType CT = clang_getCanonicalType(T); diff --git a/tools/clang-check/CMakeLists.txt b/tools/clang-check/CMakeLists.txt index e8d0d0a..2070de3 100644 --- a/tools/clang-check/CMakeLists.txt +++ b/tools/clang-check/CMakeLists.txt @@ -14,6 +14,7 @@ target_link_libraries(clang-check clangTooling clangBasic clangRewriteFrontend + clangStaticAnalyzerFrontend ) install(TARGETS clang-check diff --git a/tools/clang-check/ClangCheck.cpp b/tools/clang-check/ClangCheck.cpp index bf43374..701db52 100644 --- a/tools/clang-check/ClangCheck.cpp +++ b/tools/clang-check/ClangCheck.cpp @@ -17,10 +17,10 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ASTConsumer.h" -#include "clang/Driver/OptTable.h" #include "clang/Driver/Options.h" #include "clang/Frontend/ASTConsumers.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "clang/Rewrite/Frontend/FixItRewriter.h" #include "clang/Rewrite/Frontend/FrontendActions.h" #include "clang/Tooling/CommonOptionsParser.h" @@ -28,10 +28,12 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Path.h" #include "llvm/Support/Signals.h" +#include "llvm/Option/OptTable.h" using namespace clang::driver; using namespace clang::tooling; using namespace llvm; +using namespace llvm::opt; static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); static cl::extrahelp MoreHelp( @@ -62,6 +64,9 @@ static cl::opt<bool> ASTPrint( static cl::opt<std::string> ASTDumpFilter( "ast-dump-filter", cl::desc(Options->getOptionHelpText(options::OPT_ast_dump_filter))); +static cl::opt<bool> Analyze( + "analyze", + cl::desc(Options->getOptionHelpText(options::OPT_analyze))); static cl::opt<bool> Fixit( "fixit", @@ -70,6 +75,11 @@ static cl::opt<bool> FixWhatYouCan( "fix-what-you-can", cl::desc(Options->getOptionHelpText(options::OPT_fix_what_you_can))); +static cl::list<std::string> ArgsAfter("extra-arg", + cl::desc("Additional argument to append to the compiler command line")); +static cl::list<std::string> ArgsBefore("extra-arg-before", + cl::desc("Additional argument to prepend to the compiler command line")); + namespace { // FIXME: Move FixItRewriteInPlace from lib/Rewrite/Frontend/FrontendActions.cpp @@ -123,6 +133,39 @@ public: } }; +class InsertAdjuster: public clang::tooling::ArgumentsAdjuster { +public: + enum Position { BEGIN, END }; + + InsertAdjuster(const CommandLineArguments &Extra, Position Pos) + : Extra(Extra), Pos(Pos) { + } + + InsertAdjuster(const char *Extra, Position Pos) + : Extra(1, std::string(Extra)), Pos(Pos) { + } + + virtual CommandLineArguments + Adjust(const CommandLineArguments &Args) LLVM_OVERRIDE { + CommandLineArguments Return(Args); + + CommandLineArguments::iterator I; + if (Pos == END) { + I = Return.end(); + } else { + I = Return.begin(); + ++I; // To leave the program name in place + } + + Return.insert(I, Extra.begin(), Extra.end()); + return Return; + } + +private: + const CommandLineArguments Extra; + const Position Pos; +}; + } // namespace // Anonymous namespace here causes problems with gcc <= 4.4 on MacOS 10.6. @@ -147,8 +190,34 @@ int main(int argc, const char **argv) { CommonOptionsParser OptionsParser(argc, argv); ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); - if (Fixit) - return Tool.run(newFrontendActionFactory<FixItAction>()); - clang_check::ClangCheckActionFactory Factory; - return Tool.run(newFrontendActionFactory(&Factory)); + + // Clear adjusters because -fsyntax-only is inserted by the default chain. + Tool.clearArgumentsAdjusters(); + Tool.appendArgumentsAdjuster(new ClangStripOutputAdjuster()); + if (ArgsAfter.size() > 0) { + Tool.appendArgumentsAdjuster(new InsertAdjuster(ArgsAfter, + InsertAdjuster::END)); + } + if (ArgsBefore.size() > 0) { + Tool.appendArgumentsAdjuster(new InsertAdjuster(ArgsBefore, + InsertAdjuster::BEGIN)); + } + + // Running the analyzer requires --analyze. Other modes can work with the + // -fsyntax-only option. + Tool.appendArgumentsAdjuster(new InsertAdjuster( + Analyze ? "--analyze" : "-fsyntax-only", InsertAdjuster::BEGIN)); + + clang_check::ClangCheckActionFactory CheckFactory; + FrontendActionFactory *FrontendFactory; + + // Choose the correct factory based on the selected mode. + if (Analyze) + FrontendFactory = newFrontendActionFactory<clang::ento::AnalysisAction>(); + else if (Fixit) + FrontendFactory = newFrontendActionFactory<FixItAction>(); + else + FrontendFactory = newFrontendActionFactory(&CheckFactory); + + return Tool.run(FrontendFactory); } diff --git a/tools/clang-check/Makefile b/tools/clang-check/Makefile index 7d6505e..cf088d2 100644 --- a/tools/clang-check/Makefile +++ b/tools/clang-check/Makefile @@ -15,10 +15,11 @@ TOOLNAME = clang-check TOOL_NO_EXPORTS = 1 include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangFrontend.a clangSerialization.a clangDriver.a \ - clangTooling.a clangParse.a clangSema.a clangAnalysis.a \ - clangRewriteFrontend.a clangRewriteCore.a clangEdit.a clangAST.a \ - clangLex.a clangBasic.a + clangTooling.a clangParse.a clangSema.a \ + clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ + clangStaticAnalyzerCore.a clangAnalysis.a clangRewriteFrontend.a \ + clangRewriteCore.a clangEdit.a clangAST.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/tools/clang-format-vs/ClangFormat.sln b/tools/clang-format-vs/ClangFormat.sln new file mode 100644 index 0000000..d6b211f --- /dev/null +++ b/tools/clang-format-vs/ClangFormat.sln @@ -0,0 +1,20 @@ +
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClangFormat", "ClangFormat\ClangFormat.csproj", "{7FD1783E-2D31-4D05-BF23-6EBE1B42B608}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/tools/clang-format-vs/ClangFormat/ClangFormat.csproj b/tools/clang-format-vs/ClangFormat/ClangFormat.csproj new file mode 100644 index 0000000..65ccaba --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/ClangFormat.csproj @@ -0,0 +1,227 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{7FD1783E-2D31-4D05-BF23-6EBE1B42B608}</ProjectGuid>
+ <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>LLVM.ClangFormat</RootNamespace>
+ <AssemblyName>ClangFormat</AssemblyName>
+ <SignAssembly>true</SignAssembly>
+ <AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <UpgradeBackupLocation>
+ </UpgradeBackupLocation>
+ <OldToolsVersion>4.0</OldToolsVersion>
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <RunCodeAnalysis>true</RunCodeAnalysis>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="Microsoft.VisualStudio.CoreUtility, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Editor, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.OLE.Interop" />
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop" />
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.8.0" />
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.9.0" />
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.10.0" />
+ <Reference Include="Microsoft.VisualStudio.TextManager.Interop" />
+ <Reference Include="Microsoft.VisualStudio.Shell.10.0">
+ <Private>false</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.Data, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Text.Logic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Text.UI, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Text.UI.Wpf, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.TextManager.Interop" />
+ <Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0" />
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Design" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Windows.Forms" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Xml.Linq" />
+ </ItemGroup>
+ <ItemGroup>
+ <COMReference Include="EnvDTE">
+ <Guid>{80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2}</Guid>
+ <VersionMajor>8</VersionMajor>
+ <VersionMinor>0</VersionMinor>
+ <Lcid>0</Lcid>
+ <WrapperTool>primary</WrapperTool>
+ <Isolated>False</Isolated>
+ <EmbedInteropTypes>False</EmbedInteropTypes>
+ </COMReference>
+ <COMReference Include="EnvDTE100">
+ <Guid>{26AD1324-4B7C-44BC-84F8-B86AED45729F}</Guid>
+ <VersionMajor>10</VersionMajor>
+ <VersionMinor>0</VersionMinor>
+ <Lcid>0</Lcid>
+ <WrapperTool>primary</WrapperTool>
+ <Isolated>False</Isolated>
+ <EmbedInteropTypes>False</EmbedInteropTypes>
+ </COMReference>
+ <COMReference Include="EnvDTE80">
+ <Guid>{1A31287A-4D7D-413E-8E32-3B374931BD89}</Guid>
+ <VersionMajor>8</VersionMajor>
+ <VersionMinor>0</VersionMinor>
+ <Lcid>0</Lcid>
+ <WrapperTool>primary</WrapperTool>
+ <Isolated>False</Isolated>
+ <EmbedInteropTypes>False</EmbedInteropTypes>
+ </COMReference>
+ <COMReference Include="EnvDTE90">
+ <Guid>{2CE2370E-D744-4936-A090-3FFFE667B0E1}</Guid>
+ <VersionMajor>9</VersionMajor>
+ <VersionMinor>0</VersionMinor>
+ <Lcid>0</Lcid>
+ <WrapperTool>primary</WrapperTool>
+ <Isolated>False</Isolated>
+ <EmbedInteropTypes>False</EmbedInteropTypes>
+ </COMReference>
+ <COMReference Include="Microsoft.VisualStudio.CommandBars">
+ <Guid>{1CBA492E-7263-47BB-87FE-639000619B15}</Guid>
+ <VersionMajor>8</VersionMajor>
+ <VersionMinor>0</VersionMinor>
+ <Lcid>0</Lcid>
+ <WrapperTool>primary</WrapperTool>
+ <Isolated>False</Isolated>
+ <EmbedInteropTypes>False</EmbedInteropTypes>
+ </COMReference>
+ <COMReference Include="stdole">
+ <Guid>{00020430-0000-0000-C000-000000000046}</Guid>
+ <VersionMajor>2</VersionMajor>
+ <VersionMinor>0</VersionMinor>
+ <Lcid>0</Lcid>
+ <WrapperTool>primary</WrapperTool>
+ <Isolated>False</Isolated>
+ <EmbedInteropTypes>False</EmbedInteropTypes>
+ </COMReference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Guids.cs" />
+ <Compile Include="Resources.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>Resources.resx</DependentUpon>
+ </Compile>
+ <Compile Include="GlobalSuppressions.cs" />
+ <Compile Include="ClangFormatPackage.cs">
+ <SubType>Component</SubType>
+ </Compile>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="PkgCmdID.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Resources.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
+ <EmbeddedResource Include="VSPackage.resx">
+ <MergeWithCTO>true</MergeWithCTO>
+ <ManifestResourceName>VSPackage</ManifestResourceName>
+ </EmbeddedResource>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Key.snk" />
+ <None Include="source.extension.vsixmanifest">
+ <SubType>Designer</SubType>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <VSCTCompile Include="ClangFormat.vsct">
+ <ResourceName>Menus.ctmenu</ResourceName>
+ </VSCTCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Resources\Images_32bit.bmp" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="Resources\Package.ico" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include=".NETFramework,Version=v4.0">
+ <Visible>False</Visible>
+ <ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.4.5">
+ <Visible>False</Visible>
+ <ProductName>Windows Installer 4.5</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <PropertyGroup>
+ <UseCodebase>true</UseCodebase>
+ </PropertyGroup>
+ <PropertyGroup>
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ </PropertyGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\VSSDK\Microsoft.VsSDK.targets" Condition="false" />
+ <PropertyGroup>
+ <PreBuildEvent>if not exist $(ProjectDir)Key.snk (
+ "$(FrameworkSDKDir)Bin\NETFX 4.0 Tools\sn.exe" -k $(ProjectDir)Key.snk
+)</PreBuildEvent>
+ </PropertyGroup>
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file diff --git a/tools/clang-format-vs/ClangFormat/ClangFormat.vsct b/tools/clang-format-vs/ClangFormat/ClangFormat.vsct new file mode 100644 index 0000000..3e3e22e --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/ClangFormat.vsct @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?>
+<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+ <!-- This is the file that defines the actual layout and type of the commands.
+ It is divided in different sections (e.g. command definition, command
+ placement, ...), with each defining a specific set of properties.
+ See the comment before each section for more details about how to
+ use it. -->
+
+ <!-- The VSCT compiler (the tool that translates this file into the binary
+ format that VisualStudio will consume) has the ability to run a preprocessor
+ on the vsct file; this preprocessor is (usually) the C++ preprocessor, so
+ it is possible to define includes and macros with the same syntax used
+ in C++ files. Using this ability of the compiler here, we include some files
+ defining some of the constants that we will use inside the file. -->
+
+ <!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->
+ <Extern href="stdidcmd.h"/>
+
+ <!--This header contains the command ids for the menus provided by the shell. -->
+ <Extern href="vsshlids.h"/>
+
+
+
+
+ <!--The Commands section is where we the commands, menus and menu groups are defined.
+ This section uses a Guid to identify the package that provides the command defined inside it. -->
+ <Commands package="guidClangFormatPkg">
+ <!-- Inside this section we have different sub-sections: one for the menus, another
+ for the menu groups, one for the buttons (the actual commands), one for the combos
+ and the last one for the bitmaps used. Each element is identified by a command id that
+ is a unique pair of guid and numeric identifier; the guid part of the identifier is usually
+ called "command set" and is used to group different command inside a logically related
+ group; your package should define its own command set in order to avoid collisions
+ with command ids defined by other packages. -->
+
+
+ <!-- In this section you can define new menu groups. A menu group is a container for
+ other menus or buttons (commands); from a visual point of view you can see the
+ group as the part of a menu contained between two lines. The parent of a group
+ must be a menu. -->
+ <Groups>
+
+ <Group guid="guidClangFormatCmdSet" id="MyMenuGroup" priority="0x0600">
+ <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
+ </Group>
+
+
+
+ </Groups>
+
+ <!--Buttons section. -->
+ <!--This section defines the elements the user can interact with, like a menu command or a button
+ or combo box in a toolbar. -->
+ <Buttons>
+ <!--To define a menu group you have to specify its ID, the parent menu and its display priority.
+ The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use
+ the CommandFlag node.
+ You can add more than one CommandFlag node e.g.:
+ <CommandFlag>DefaultInvisible</CommandFlag>
+ <CommandFlag>DynamicVisibility</CommandFlag>
+ If you do not want an image next to your command, remove the Icon node /> -->
+
+ <Button guid="guidClangFormatCmdSet" id="cmdidClangFormat" priority="0x0100" type="Button">
+ <Parent guid="guidClangFormatCmdSet" id="MyMenuGroup" />
+ <Icon guid="guidImages" id="bmpPic1" />
+ <Strings>
+ <ButtonText>ClangFormat</ButtonText>
+ </Strings>
+ </Button>
+
+
+
+ </Buttons>
+
+ <!--The bitmaps section is used to define the bitmaps that are used for the commands.-->
+ <Bitmaps>
+ <!-- The bitmap id is defined in a way that is a little bit different from the others:
+ the declaration starts with a guid for the bitmap strip, then there is the resource id of the
+ bitmap strip containing the bitmaps and then there are the numeric ids of the elements used
+ inside a button definition. An important aspect of this declaration is that the element id
+ must be the actual index (1-based) of the bitmap inside the bitmap strip. -->
+ <Bitmap guid="guidImages" href="Resources\Images_32bit.bmp" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>
+
+ </Bitmaps>
+
+ </Commands>
+
+
+ <KeyBindings>
+ <KeyBinding guid="guidClangFormatCmdSet" id="cmdidClangFormat" editor="guidTextEditor" key1="R" mod1="Control" key2="F" mod2="Control"/>
+ </KeyBindings>
+
+
+
+ <Symbols>
+ <!-- This is the package guid. -->
+ <GuidSymbol name="guidClangFormatPkg" value="{c5286038-25d3-4f65-83a8-51fa2df4a146}" />
+
+ <!-- This is the guid used to group the menu commands together -->
+ <GuidSymbol name="guidClangFormatCmdSet" value="{e39cbab1-0f96-4022-a2bc-da5a9db7eb78}">
+
+ <IDSymbol name="MyMenuGroup" value="0x1020" />
+ <IDSymbol name="cmdidClangFormat" value="0x0100" />
+ </GuidSymbol>
+
+ <GuidSymbol name="guidTextEditor" value="{8B382828-6202-11d1-8870-0000F87579D2}" />
+
+
+ <GuidSymbol name="guidImages" value="{6d53937b-9ae1-42e1-8849-d876dcdbad7b}" >
+ <IDSymbol name="bmpPic1" value="1" />
+ <IDSymbol name="bmpPic2" value="2" />
+ <IDSymbol name="bmpPicSearch" value="3" />
+ <IDSymbol name="bmpPicX" value="4" />
+ <IDSymbol name="bmpPicArrows" value="5" />
+ </GuidSymbol>
+ </Symbols>
+
+</CommandTable>
diff --git a/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs b/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs new file mode 100644 index 0000000..797d467 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs @@ -0,0 +1,220 @@ +//===-- ClangFormatPackages.cs - VSPackage for clang-format ------*- C# -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This class contains a VS extension package that runs clang-format over a
+// selection in a VS text editor.
+//
+//===----------------------------------------------------------------------===//
+
+using Microsoft.VisualStudio.Editor;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.TextManager.Interop;
+using System;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Xml.Linq;
+
+namespace LLVM.ClangFormat
+{
+ [ClassInterface(ClassInterfaceType.AutoDual)]
+ [CLSCompliant(false), ComVisible(true)]
+ public class OptionPageGrid : DialogPage
+ {
+ private string style = "File";
+
+ [Category("LLVM/Clang")]
+ [DisplayName("Style")]
+ [Description("Coding style, currently supports:\n" +
+ " - Predefined styles ('LLVM', 'Google', 'Chromium', 'Mozilla').\n" +
+ " - 'File' to search for a YAML .clang-format or _clang-format\n" +
+ " configuration file.\n" +
+ " - A YAML configuration snippet.\n\n" +
+ "'File':\n" +
+ " Searches for a .clang-format or _clang-format configuration file\n" +
+ " in the source file's directory and its parents.\n\n" +
+ "YAML configuration snippet:\n" +
+ " The content of a .clang-format configuration file, as string.\n" +
+ " Example: '{BasedOnStyle: \"LLVM\", IndentWidth: 8}'\n\n" +
+ "See also: http://clang.llvm.org/docs/ClangFormatStyleOptions.html.")]
+ public string Style
+ {
+ get { return style; }
+ set { style = value; }
+ }
+ }
+
+ [PackageRegistration(UseManagedResourcesOnly = true)]
+ [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
+ [ProvideMenuResource("Menus.ctmenu", 1)]
+ [Guid(GuidList.guidClangFormatPkgString)]
+ [ProvideOptionPage(typeof(OptionPageGrid), "LLVM/Clang", "ClangFormat", 0, 0, true)]
+ public sealed class ClangFormatPackage : Package
+ {
+ #region Package Members
+ protected override void Initialize()
+ {
+ base.Initialize();
+
+ var commandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
+ if (commandService != null)
+ {
+ var menuCommandID = new CommandID(GuidList.guidClangFormatCmdSet, (int)PkgCmdIDList.cmdidClangFormat);
+ var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
+ commandService.AddCommand(menuItem);
+ }
+ }
+ #endregion
+
+ private void MenuItemCallback(object sender, EventArgs args)
+ {
+ IWpfTextView view = GetCurrentView();
+ if (view == null)
+ // We're not in a text view.
+ return;
+ string text = view.TextBuffer.CurrentSnapshot.GetText();
+ int start = view.Selection.Start.Position.GetContainingLine().Start.Position;
+ int end = view.Selection.End.Position.GetContainingLine().End.Position;
+ int length = end - start;
+ // clang-format doesn't support formatting a range that starts at the end
+ // of the file.
+ if (start >= text.Length && text.Length > 0)
+ start = text.Length - 1;
+ string path = GetDocumentParent(view);
+ try
+ {
+ var root = XElement.Parse(RunClangFormat(text, start, length, path));
+ var edit = view.TextBuffer.CreateEdit();
+ foreach (XElement replacement in root.Descendants("replacement"))
+ {
+ var span = new Span(
+ int.Parse(replacement.Attribute("offset").Value),
+ int.Parse(replacement.Attribute("length").Value));
+ edit.Replace(span, replacement.Value);
+ }
+ edit.Apply();
+ }
+ catch (Exception e)
+ {
+ var uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
+ var id = Guid.Empty;
+ int result;
+ uiShell.ShowMessageBox(
+ 0, ref id,
+ "Error while running clang-format:",
+ e.Message,
+ string.Empty, 0,
+ OLEMSGBUTTON.OLEMSGBUTTON_OK,
+ OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
+ OLEMSGICON.OLEMSGICON_INFO,
+ 0, out result);
+ }
+ }
+
+ /// <summary>
+ /// Runs the given text through clang-format and returns the replacements as XML.
+ ///
+ /// Formats the text range starting at offset of the given length.
+ /// </summary>
+ private string RunClangFormat(string text, int offset, int length, string path)
+ {
+ System.Diagnostics.Process process = new System.Diagnostics.Process();
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.FileName = "clang-format.exe";
+ // Poor man's escaping - this will not work when quotes are already escaped
+ // in the input (but we don't need more).
+ string style = GetStyle().Replace("\"", "\\\"");
+ process.StartInfo.Arguments = " -offset " + offset +
+ " -length " + length +
+ " -output-replacements-xml " +
+ " -style \"" + style + "\"";
+ process.StartInfo.CreateNoWindow = true;
+ process.StartInfo.RedirectStandardInput = true;
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.RedirectStandardError = true;
+ if (path != null)
+ process.StartInfo.WorkingDirectory = path;
+ // We have to be careful when communicating via standard input / output,
+ // as writes to the buffers will block until they are read from the other side.
+ // Thus, we:
+ // 1. Start the process - clang-format.exe will start to read the input from the
+ // standard input.
+ try
+ {
+ process.Start();
+ }
+ catch (Exception e)
+ {
+ throw new Exception(
+ "Cannot execute " + process.StartInfo.FileName + ".\n\"" +
+ e.Message + "\".\nPlease make sure it is on the PATH.");
+ }
+ // 2. We write everything to the standard output - this cannot block, as clang-format
+ // reads the full standard input before analyzing it without writing anything to the
+ // standard output.
+ process.StandardInput.Write(text);
+ // 3. We notify clang-format that the input is done - after this point clang-format
+ // will start analyzing the input and eventually write the output.
+ process.StandardInput.Close();
+ // 4. We must read clang-format's output before waiting for it to exit; clang-format
+ // will close the channel by exiting.
+ string output = process.StandardOutput.ReadToEnd();
+ // 5. clang-format is done, wait until it is fully shut down.
+ process.WaitForExit();
+ if (process.ExitCode != 0)
+ {
+ // FIXME: If clang-format writes enough to the standard error stream to block,
+ // we will never reach this point; instead, read the standard error asynchronously.
+ throw new Exception(process.StandardError.ReadToEnd());
+ }
+ return output;
+ }
+
+ /// <summary>
+ /// Returns the currently active view if it is a IWpfTextView.
+ /// </summary>
+ private IWpfTextView GetCurrentView()
+ {
+ // The SVsTextManager is a service through which we can get the active view.
+ var textManager = (IVsTextManager)Package.GetGlobalService(typeof(SVsTextManager));
+ IVsTextView textView;
+ textManager.GetActiveView(1, null, out textView);
+
+ // Now we have the active view as IVsTextView, but the text interfaces we need
+ // are in the IWpfTextView.
+ var userData = (IVsUserData)textView;
+ if (userData == null)
+ return null;
+ Guid guidWpfViewHost = DefGuidList.guidIWpfTextViewHost;
+ object host;
+ userData.GetData(ref guidWpfViewHost, out host);
+ return ((IWpfTextViewHost)host).TextView;
+ }
+
+ private string GetStyle()
+ {
+ var page = (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid));
+ return page.Style;
+ }
+
+ private string GetDocumentParent(IWpfTextView view)
+ {
+ ITextDocument document;
+ if (view.TextBuffer.Properties.TryGetProperty(typeof(ITextDocument), out document))
+ {
+ return Directory.GetParent(document.FilePath).ToString();
+ }
+ return null;
+ }
+ }
+}
diff --git a/tools/clang-format-vs/ClangFormat/GlobalSuppressions.cs b/tools/clang-format-vs/ClangFormat/GlobalSuppressions.cs new file mode 100644 index 0000000..175a74e --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/GlobalSuppressions.cs @@ -0,0 +1,11 @@ +// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project. Project-level
+// suppressions either have no target or are given a specific target
+// and scoped to a namespace, type, member, etc.
+//
+// To add a suppression to this file, right-click the message in the
+// Error List, point to "Suppress Message(s)", and click "In Project
+// Suppression File". You do not need to add suppressions to this
+// file manually.
+
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")]
diff --git a/tools/clang-format-vs/ClangFormat/Guids.cs b/tools/clang-format-vs/ClangFormat/Guids.cs new file mode 100644 index 0000000..c045224 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/Guids.cs @@ -0,0 +1,12 @@ +using System;
+
+namespace LLVM.ClangFormat
+{
+ static class GuidList
+ {
+ public const string guidClangFormatPkgString = "c5286038-25d3-4f65-83a8-51fa2df4a146";
+ public const string guidClangFormatCmdSetString = "e39cbab1-0f96-4022-a2bc-da5a9db7eb78";
+
+ public static readonly Guid guidClangFormatCmdSet = new Guid(guidClangFormatCmdSetString);
+ };
+}
\ No newline at end of file diff --git a/tools/clang-format-vs/ClangFormat/PkgCmdID.cs b/tools/clang-format-vs/ClangFormat/PkgCmdID.cs new file mode 100644 index 0000000..bb6b455 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/PkgCmdID.cs @@ -0,0 +1,7 @@ +namespace LLVM.ClangFormat
+{
+ static class PkgCmdIDList
+ {
+ public const uint cmdidClangFormat = 0x100;
+ };
+}
\ No newline at end of file diff --git a/tools/clang-format-vs/ClangFormat/Properties/AssemblyInfo.cs b/tools/clang-format-vs/ClangFormat/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e6e4de4 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System;
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ClangFormat")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("LLVM")]
+[assembly: AssemblyProduct("ClangFormat")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: ComVisible(false)]
+[assembly: CLSCompliant(false)]
+[assembly: NeutralResourcesLanguage("en-US")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/tools/clang-format-vs/ClangFormat/Resources.Designer.cs b/tools/clang-format-vs/ClangFormat/Resources.Designer.cs new file mode 100644 index 0000000..efec031 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/Resources.Designer.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.18408
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace LLVM.ClangFormat {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LLVM.ClangFormat.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ }
+}
diff --git a/tools/clang-format-vs/ClangFormat/Resources.resx b/tools/clang-format-vs/ClangFormat/Resources.resx new file mode 100644 index 0000000..352987a --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/Resources.resx @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+ VS SDK Notes: This resx file contains the resources that will be consumed directly by your package.
+ For example, if you chose to create a tool window, there is a resource with ID 'CanNotCreateWindow'. This
+ is used in VsPkg.cs to determine the string to show the user if there is an error when attempting to create
+ the tool window.
+
+ Resources that are accessed directly from your package *by Visual Studio* are stored in the VSPackage.resx
+ file.
+-->
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+</root>
\ No newline at end of file diff --git a/tools/clang-format-vs/ClangFormat/Resources/Images_32bit.bmp b/tools/clang-format-vs/ClangFormat/Resources/Images_32bit.bmp Binary files differnew file mode 100644 index 0000000..2fa7ab0 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/Resources/Images_32bit.bmp diff --git a/tools/clang-format-vs/ClangFormat/Resources/Package.ico b/tools/clang-format-vs/ClangFormat/Resources/Package.ico Binary files differnew file mode 100644 index 0000000..ea3b23f --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/Resources/Package.ico diff --git a/tools/clang-format-vs/ClangFormat/VSPackage.resx b/tools/clang-format-vs/ClangFormat/VSPackage.resx new file mode 100644 index 0000000..81102d3 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/VSPackage.resx @@ -0,0 +1,140 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+ VS SDK Notes: This resx file contains the resources that will be consumed from your package by Visual Studio.
+ For example, Visual Studio will attempt to load resource '400' from this resource stream when it needs to
+ load your package's icon. Because Visual Studio will always look in the VSPackage.resources stream first for
+ resources it needs, you should put additional resources that Visual Studio will load directly into this resx
+ file.
+
+ Resources that you would like to access directly from your package in a strong-typed fashion should be stored
+ in Resources.resx or another resx file.
+-->
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+ <data name="110" xml:space="preserve">
+ <value>ClangFormat</value>
+ </data>
+ <data name="112" xml:space="preserve">
+ <value>Formats code by calling the clang-format executable.</value>
+ </data>
+ <data name="400" type="System.Resources.ResXFileRef, System.Windows.Forms">
+ <value>Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+ </data>
+</root>
\ No newline at end of file diff --git a/tools/clang-format-vs/ClangFormat/source.extension.vsixmanifest b/tools/clang-format-vs/ClangFormat/source.extension.vsixmanifest new file mode 100644 index 0000000..39d30f0 --- /dev/null +++ b/tools/clang-format-vs/ClangFormat/source.extension.vsixmanifest @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Vsix Version="1.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
+ <Identifier Id="20dbc914-1c7a-4992-b236-ef58b37850eb">
+ <Name>ClangFormat</Name>
+ <Author>LLVM</Author>
+ <Version>1.0</Version>
+ <Description xml:space="preserve">Information about my package</Description>
+ <Locale>1033</Locale>
+ <InstalledByMsi>false</InstalledByMsi>
+ <SupportedProducts>
+ <VisualStudio Version="10.0">
+ <Edition>Pro</Edition>
+ </VisualStudio>
+ <VisualStudio Version="11.0">
+ <Edition>Pro</Edition>
+ </VisualStudio>
+ <VisualStudio Version="12.0">
+ <Edition>Pro</Edition>
+ </VisualStudio>
+ </SupportedProducts>
+ <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />
+ </Identifier>
+ <References>
+ <Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">
+ <Name>Visual Studio MPF</Name>
+ </Reference>
+ </References>
+ <Content>
+ <VsPackage>|%CurrentProject%;PkgdefProjectOutputGroup|</VsPackage>
+ </Content>
+</Vsix>
diff --git a/tools/clang-format-vs/README.txt b/tools/clang-format-vs/README.txt new file mode 100644 index 0000000..6d4ebb3 --- /dev/null +++ b/tools/clang-format-vs/README.txt @@ -0,0 +1,6 @@ +This directory contains a VSPackage project to generate a Visual Studio extension
+for clang-format.
+
+Build prerequisites are:
+- Visual Studio 2012 Professional
+- Visual Studio SDK (http://www.microsoft.com/en-us/download/details.aspx?id=30668)
diff --git a/tools/clang-format/CMakeLists.txt b/tools/clang-format/CMakeLists.txt index c86a920..7bb3fbf 100644 --- a/tools/clang-format/CMakeLists.txt +++ b/tools/clang-format/CMakeLists.txt @@ -12,6 +12,10 @@ target_link_libraries(clang-format clangRewriteFrontend ) -install(TARGETS clang-format - RUNTIME DESTINATION bin) - +install(TARGETS clang-format RUNTIME DESTINATION bin) +install(PROGRAMS clang-format-bbedit.applescript DESTINATION share/clang) +install(PROGRAMS clang-format-diff.py DESTINATION share/clang) +install(PROGRAMS clang-format-sublime.py DESTINATION share/clang) +install(PROGRAMS clang-format.el DESTINATION share/clang) +install(PROGRAMS clang-format.py DESTINATION share/clang) +install(PROGRAMS git-clang-format DESTINATION bin) diff --git a/tools/clang-format/ClangFormat.cpp b/tools/clang-format/ClangFormat.cpp index 57833ed..768165b 100644 --- a/tools/clang-format/ClangFormat.cpp +++ b/tools/clang-format/ClangFormat.cpp @@ -20,32 +20,76 @@ #include "clang/Format/Format.h" #include "clang/Lex/Lexer.h" #include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Signals.h" +#include "llvm/ADT/StringMap.h" using namespace llvm; static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); +// Mark all our options with this category, everything else (except for -version +// and -help) will be hidden. +cl::OptionCategory ClangFormatCategory("Clang-format options"); + static cl::list<unsigned> -Offsets("offset", cl::desc("Format a range starting at this file offset. Can " - "only be used with one input file.")); + Offsets("offset", + cl::desc("Format a range starting at this byte offset.\n" + "Multiple ranges can be formatted by specifying\n" + "several -offset and -length pairs.\n" + "Can only be used with one input file."), + cl::cat(ClangFormatCategory)); static cl::list<unsigned> -Lengths("length", cl::desc("Format a range of this length. " - "When it's not specified, end of file is used. " - "Can only be used with one input file.")); -static cl::opt<std::string> Style( - "style", - cl::desc("Coding style, currently supports: LLVM, Google, Chromium, Mozilla."), - cl::init("LLVM")); + Lengths("length", + cl::desc("Format a range of this length (in bytes).\n" + "Multiple ranges can be formatted by specifying\n" + "several -offset and -length pairs.\n" + "When only a single -offset is specified without\n" + "-length, clang-format will format up to the end\n" + "of the file.\n" + "Can only be used with one input file."), + cl::cat(ClangFormatCategory)); +static cl::list<std::string> +LineRanges("lines", cl::desc("<start line>:<end line> - format a range of\n" + "lines (both 1-based).\n" + "Multiple ranges can be formatted by specifying\n" + "several -lines arguments.\n" + "Can't be used with -offset and -length.\n" + "Can only be used with one input file."), + cl::cat(ClangFormatCategory)); +static cl::opt<std::string> + Style("style", + cl::desc(clang::format::StyleOptionHelpDescription), + cl::init("file"), cl::cat(ClangFormatCategory)); + +static cl::opt<std::string> +AssumeFilename("assume-filename", + cl::desc("When reading from stdin, clang-format assumes this\n" + "filename to look for a style config file (with\n" + "-style=file)."), + cl::cat(ClangFormatCategory)); + static cl::opt<bool> Inplace("i", - cl::desc("Inplace edit <file>s, if specified.")); + cl::desc("Inplace edit <file>s, if specified."), + cl::cat(ClangFormatCategory)); -static cl::opt<bool> OutputXML( - "output-replacements-xml", cl::desc("Output replacements as XML.")); +static cl::opt<bool> OutputXML("output-replacements-xml", + cl::desc("Output replacements as XML."), + cl::cat(ClangFormatCategory)); +static cl::opt<bool> + DumpConfig("dump-config", + cl::desc("Dump configuration options to stdout and exit.\n" + "Can be used with -style option."), + cl::cat(ClangFormatCategory)); +static cl::opt<unsigned> + Cursor("cursor", + cl::desc("The position of the cursor when invoking\n" + "clang-format from an editor integration"), + cl::init(0), cl::cat(ClangFormatCategory)); -static cl::list<std::string> FileNames(cl::Positional, - cl::desc("[<file> ...]")); +static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"), + cl::cat(ClangFormatCategory)); namespace clang { namespace format { @@ -59,34 +103,43 @@ static FileID createInMemoryFile(StringRef FileName, const MemoryBuffer *Source, return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User); } -static FormatStyle getStyle() { - FormatStyle TheStyle = getGoogleStyle(); - if (Style == "LLVM") - TheStyle = getLLVMStyle(); - else if (Style == "Chromium") - TheStyle = getChromiumStyle(); - else if (Style == "Mozilla") - TheStyle = getMozillaStyle(); - else if (Style != "Google") - llvm::errs() << "Unknown style " << Style << ", using Google style.\n"; - - return TheStyle; +// Parses <start line>:<end line> input to a pair of line numbers. +// Returns true on error. +static bool parseLineRange(StringRef Input, unsigned &FromLine, + unsigned &ToLine) { + std::pair<StringRef, StringRef> LineRange = Input.split(':'); + return LineRange.first.getAsInteger(0, FromLine) || + LineRange.second.getAsInteger(0, ToLine); } -// Returns true on error. -static bool format(std::string FileName) { - FileManager Files((FileSystemOptions())); - DiagnosticsEngine Diagnostics( - IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), - new DiagnosticOptions); - SourceManager Sources(Diagnostics, Files); - OwningPtr<MemoryBuffer> Code; - if (error_code ec = MemoryBuffer::getFileOrSTDIN(FileName, Code)) { - llvm::errs() << ec.message() << "\n"; - return true; +static bool fillRanges(SourceManager &Sources, FileID ID, + const MemoryBuffer *Code, + std::vector<CharSourceRange> &Ranges) { + if (!LineRanges.empty()) { + if (!Offsets.empty() || !Lengths.empty()) { + llvm::errs() << "error: cannot use -lines with -offset/-length\n"; + return true; + } + + for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) { + unsigned FromLine, ToLine; + if (parseLineRange(LineRanges[i], FromLine, ToLine)) { + llvm::errs() << "error: invalid <start line>:<end line> pair\n"; + return true; + } + if (FromLine > ToLine) { + llvm::errs() << "error: start line should be less than end line\n"; + return true; + } + SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1); + SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX); + if (Start.isInvalid() || End.isInvalid()) + return true; + Ranges.push_back(CharSourceRange::getCharRange(Start, End)); + } + return false; } - FileID ID = createInMemoryFile(FileName, Code.get(), Sources, Files); - Lexer Lex(ID, Sources.getBuffer(ID), Sources, getFormattingLangOpts()); + if (Offsets.empty()) Offsets.push_back(0); if (Offsets.size() != Lengths.size() && @@ -95,7 +148,6 @@ static bool format(std::string FileName) { << "error: number of -offset and -length arguments must match.\n"; return true; } - std::vector<CharSourceRange> Ranges; for (unsigned i = 0, e = Offsets.size(); i != e; ++i) { if (Offsets[i] >= Code->getBufferSize()) { llvm::errs() << "error: offset " << Offsets[i] @@ -118,7 +170,33 @@ static bool format(std::string FileName) { } Ranges.push_back(CharSourceRange::getCharRange(Start, End)); } - tooling::Replacements Replaces = reformat(getStyle(), Lex, Sources, Ranges); + return false; +} + +// Returns true on error. +static bool format(StringRef FileName) { + FileManager Files((FileSystemOptions())); + DiagnosticsEngine Diagnostics( + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), + new DiagnosticOptions); + SourceManager Sources(Diagnostics, Files); + OwningPtr<MemoryBuffer> Code; + if (error_code ec = MemoryBuffer::getFileOrSTDIN(FileName, Code)) { + llvm::errs() << ec.message() << "\n"; + return true; + } + if (Code->getBufferSize() == 0) + return false; // Empty files are formatted correctly. + FileID ID = createInMemoryFile(FileName, Code.get(), Sources, Files); + std::vector<CharSourceRange> Ranges; + if (fillRanges(Sources, ID, Code.get(), Ranges)) + return true; + + FormatStyle FormatStyle = + getStyle(Style, (FileName == "-") ? AssumeFilename : FileName); + Lexer Lex(ID, Sources.getBuffer(ID), Sources, + getFormattingLangOpts(FormatStyle.Standard)); + tooling::Replacements Replaces = reformat(FormatStyle, Lex, Sources, Ranges); if (OutputXML) { llvm::outs() << "<?xml version='1.0'?>\n<replacements xml:space='preserve'>\n"; @@ -135,19 +213,12 @@ static bool format(std::string FileName) { Rewriter Rewrite(Sources, LangOptions()); tooling::applyAllReplacements(Replaces, Rewrite); if (Inplace) { - if (Replaces.size() == 0) - return false; // Nothing changed, don't touch the file. - - std::string ErrorInfo; - llvm::raw_fd_ostream FileStream(FileName.c_str(), ErrorInfo, - llvm::raw_fd_ostream::F_Binary); - if (!ErrorInfo.empty()) { - llvm::errs() << "Error while writing file: " << ErrorInfo << "\n"; + if (Rewrite.overwriteChangedFiles()) return true; - } - Rewrite.getEditBuffer(ID).write(FileStream); - FileStream.flush(); } else { + if (Cursor.getNumOccurrences() != 0) + outs() << "{ \"Cursor\": " << tooling::shiftedCodePosition( + Replaces, Cursor) << " }\n"; Rewrite.getEditBuffer(ID).write(outs()); } } @@ -159,18 +230,37 @@ static bool format(std::string FileName) { int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(); + + // Hide unrelated options. + StringMap<cl::Option*> Options; + cl::getRegisteredOptions(Options); + for (StringMap<cl::Option *>::iterator I = Options.begin(), E = Options.end(); + I != E; ++I) { + if (I->second->Category != &ClangFormatCategory && I->first() != "help" && + I->first() != "version") + I->second->setHiddenFlag(cl::ReallyHidden); + } + cl::ParseCommandLineOptions( argc, argv, "A tool to format C/C++/Obj-C code.\n\n" "If no arguments are specified, it formats the code from standard input\n" "and writes the result to the standard output.\n" - "If <file>s are given, it reformats the files. If -i is specified \n" - "together with <file>s, the files are edited in-place. Otherwise, the \n" + "If <file>s are given, it reformats the files. If -i is specified\n" + "together with <file>s, the files are edited in-place. Otherwise, the\n" "result is written to the standard output.\n"); if (Help) cl::PrintHelpMessage(); + if (DumpConfig) { + std::string Config = + clang::format::configurationAsText(clang::format::getStyle( + Style, FileNames.empty() ? AssumeFilename : FileNames[0])); + llvm::outs() << Config << "\n"; + return 0; + } + bool Error = false; switch (FileNames.size()) { case 0: @@ -180,8 +270,8 @@ int main(int argc, const char **argv) { Error = clang::format::format(FileNames[0]); break; default: - if (!Offsets.empty() || !Lengths.empty()) { - llvm::errs() << "error: \"-offset\" and \"-length\" can only be used for " + if (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty()) { + llvm::errs() << "error: -offset, -length and -lines can only be used for " "single file.\n"; return 1; } diff --git a/tools/clang-format/Makefile b/tools/clang-format/Makefile index d869267..4902244 100644 --- a/tools/clang-format/Makefile +++ b/tools/clang-format/Makefile @@ -15,7 +15,7 @@ TOOLNAME = clang-format TOOL_NO_EXPORTS = 1 include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangFormat.a clangTooling.a clangFrontend.a clangSerialization.a \ clangDriver.a clangParse.a clangSema.a clangAnalysis.a \ clangRewriteFrontend.a clangRewriteCore.a clangEdit.a clangAST.a \ diff --git a/tools/clang-format/clang-format-diff.py b/tools/clang-format/clang-format-diff.py index 68b5113..60b8fb7 100755 --- a/tools/clang-format/clang-format-diff.py +++ b/tools/clang-format/clang-format-diff.py @@ -17,13 +17,16 @@ This script reads input from a unified diff and reformats all the changed lines. This is useful to reformat all the lines touched by a specific patch. Example usage for git users: - git diff -U0 HEAD^ | clang-format-diff.py -p1 + git diff -U0 HEAD^ | clang-format-diff.py -p1 -i """ import argparse +import difflib import re +import string import subprocess +import StringIO import sys @@ -31,67 +34,24 @@ import sys binary = 'clang-format' -def getOffsetLength(filename, line_number, line_count): - """ - Calculates the field offset and length based on line number and count. - """ - offset = 0 - length = 0 - with open(filename, 'r') as f: - for line in f: - if line_number > 1: - offset += len(line) - line_number -= 1 - elif line_count > 0: - length += len(line) - line_count -= 1 - else: - break - return offset, length - - -def formatRange(r, style): - """ - Formats range 'r' according to style 'style'. - """ - filename, line_number, line_count = r - # FIXME: Add other types containing C++/ObjC code. - if not (filename.endswith(".cpp") or filename.endswith(".cc") or - filename.endswith(".h")): - return - - offset, length = getOffsetLength(filename, line_number, line_count) - with open(filename, 'r') as f: - text = f.read() - command = [binary, '-offset', str(offset), '-length', str(length)] - if style: - command.extend(['-style', style]) - p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - stdin=subprocess.PIPE) - stdout, stderr = p.communicate(input=text) - if stderr: - print stderr - return - if not stdout: - print 'Segfault occurred while formatting', filename - print 'Please report a bug on llvm.org/bugs.' - return - with open(filename, 'w') as f: - f.write(stdout) - - def main(): parser = argparse.ArgumentParser(description= - 'Reformat changed lines in diff') - parser.add_argument('-p', default=1, + 'Reformat changed lines in diff. Without -i ' + 'option just output the diff that would be' + 'introduced.') + parser.add_argument('-i', action='store_true', default=False, + help='apply edits to files instead of displaying a diff') + parser.add_argument('-p', default=0, help='strip the smallest prefix containing P slashes') - parser.add_argument('-style', - help='formatting style to apply (LLVM, Google, Chromium)') + parser.add_argument( + '-style', + help= + 'formatting style to apply (LLVM, Google, Chromium, Mozilla, WebKit)') args = parser.parse_args() + # Extract changed lines for each file. filename = None - ranges = [] - + lines_by_file = {} for line in sys.stdin: match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line) if match: @@ -99,18 +59,50 @@ def main(): if filename == None: continue + # FIXME: Add other types containing C++/ObjC code. + if not (filename.endswith(".cpp") or filename.endswith(".cc") or + filename.endswith(".h")): + continue + match = re.search('^@@.*\+(\d+)(,(\d+))?', line) if match: + start_line = int(match.group(1)) line_count = 1 if match.group(3): line_count = int(match.group(3)) - ranges.append((filename, int(match.group(1)), line_count)) - - # Reverse the ranges so that the reformatting does not influence file offsets. - for r in reversed(ranges): - # Do the actual formatting. - formatRange(r, args.style) - + if line_count == 0: + continue + end_line = start_line + line_count - 1; + lines_by_file.setdefault(filename, []).extend( + ['-lines', str(start_line) + ':' + str(end_line)]) + + # Reformat files containing changes in place. + for filename, lines in lines_by_file.iteritems(): + command = [binary, filename] + if args.i: + command.append('-i') + command.extend(lines) + if args.style: + command.extend(['-style', args.style]) + p = subprocess.Popen(command, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE) + stdout, stderr = p.communicate() + if stderr: + print stderr + if p.returncode != 0: + sys.exit(p.returncode); + + if not args.i: + with open(filename) as f: + code = f.readlines() + formatted_code = StringIO.StringIO(stdout).readlines() + diff = difflib.unified_diff(code, formatted_code, + filename, filename, + '(before formatting)', '(after formatting)') + diff_string = string.join(diff, '') + if len(diff_string) > 0: + print diff_string if __name__ == '__main__': main() diff --git a/tools/clang-format/clang-format-sublime.py b/tools/clang-format/clang-format-sublime.py new file mode 100644 index 0000000..16ff56e --- /dev/null +++ b/tools/clang-format/clang-format-sublime.py @@ -0,0 +1,58 @@ +# This file is a minimal clang-format sublime-integration. To install: +# - Change 'binary' if clang-format is not on the path (see below). +# - Put this file into your sublime Packages directory, e.g. on Linux: +# ~/.config/sublime-text-2/Packages/User/clang-format-sublime.py +# - Add a key binding: +# { "keys": ["ctrl+shift+c"], "command": "clang_format" }, +# +# With this integration you can press the bound key and clang-format will +# format the current lines and selections for all cursor positions. The lines +# or regions are extended to the next bigger syntactic entities. +# +# It operates on the current, potentially unsaved buffer and does not create +# or save any files. To revert a formatting, just undo. + +from __future__ import print_function +import sublime +import sublime_plugin +import subprocess + +# Change this to the full path if clang-format is not on the path. +binary = 'clang-format' + +# Change this to format according to other formatting styles. See the output of +# 'clang-format --help' for a list of supported styles. The default looks for +# a '.clang-format' or '_clang-format' file to indicate the style that should be +# used. +style = 'file' + +class ClangFormatCommand(sublime_plugin.TextCommand): + def run(self, edit): + encoding = self.view.encoding() + if encoding == 'Undefined': + encoding = 'utf-8' + regions = [] + command = [binary, '-style', style] + for region in self.view.sel(): + regions.append(region) + region_offset = min(region.a, region.b) + region_length = abs(region.b - region.a) + command.extend(['-offset', str(region_offset), + '-length', str(region_length), + '-assume-filename', str(self.view.file_name())]) + old_viewport_position = self.view.viewport_position() + buf = self.view.substr(sublime.Region(0, self.view.size())) + p = subprocess.Popen(command, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, stdin=subprocess.PIPE) + output, error = p.communicate(buf.encode(encoding)) + if error: + print(error) + self.view.replace( + edit, sublime.Region(0, self.view.size()), + output.decode(encoding)) + self.view.sel().clear() + for region in regions: + self.view.sel().add(region) + # FIXME: Without the 10ms delay, the viewport sometimes jumps. + sublime.set_timeout(lambda: self.view.set_viewport_position( + old_viewport_position, False), 10) diff --git a/tools/clang-format/clang-format.el b/tools/clang-format/clang-format.el index 2c5546b..520a3e2 100644 --- a/tools/clang-format/clang-format.el +++ b/tools/clang-format/clang-format.el @@ -7,25 +7,50 @@ ;; (global-set-key [C-M-tab] 'clang-format-region) ;; ;; Depending on your configuration and coding style, you might need to modify -;; 'style' and 'binary' below. +;; 'style' in clang-format, below. + +(require 'json) + +;; *Location of the clang-format binary. If it is on your PATH, a full path name +;; need not be specified. +(defvar clang-format-binary "clang-format") + (defun clang-format-region () + "Use clang-format to format the currently active region." + (interactive) + (let ((beg (if mark-active + (region-beginning) + (min (line-beginning-position) (1- (point-max))))) + (end (if mark-active + (region-end) + (line-end-position)))) + (clang-format beg end))) + +(defun clang-format-buffer () + "Use clang-format to format the current buffer." (interactive) + (clang-format (point-min) (point-max))) +(defun clang-format (begin end) + "Use clang-format to format the code between BEGIN and END." (let* ((orig-windows (get-buffer-window-list (current-buffer))) (orig-window-starts (mapcar #'window-start orig-windows)) (orig-point (point)) - (binary "clang-format") - (style "LLVM")) - (if mark-active - (setq beg (region-beginning) - end (region-end)) - (setq beg (min (line-beginning-position) (1- (point-max))) - end (min (line-end-position) (1- (point-max))))) - (call-process-region (point-min) (point-max) binary t t nil - "-offset" (number-to-string (1- beg)) - "-length" (number-to-string (- end beg)) - "-style" style) - (goto-char orig-point) - (dotimes (index (length orig-windows)) - (set-window-start (nth index orig-windows) - (nth index orig-window-starts))))) + (style "file")) + (unwind-protect + (call-process-region (point-min) (point-max) clang-format-binary + t (list t nil) nil + "-offset" (number-to-string (1- begin)) + "-length" (number-to-string (- end begin)) + "-cursor" (number-to-string (1- (point))) + "-assume-filename" (buffer-file-name) + "-style" style) + (goto-char (point-min)) + (let ((json-output (json-read-from-string + (buffer-substring-no-properties + (point-min) (line-beginning-position 2))))) + (delete-region (point-min) (line-beginning-position 2)) + (goto-char (1+ (cdr (assoc 'Cursor json-output)))) + (dotimes (index (length orig-windows)) + (set-window-start (nth index orig-windows) + (nth index orig-window-starts))))))) diff --git a/tools/clang-format/clang-format.py b/tools/clang-format/clang-format.py index d90c62a..f5a5756 100644 --- a/tools/clang-format/clang-format.py +++ b/tools/clang-format/clang-format.py @@ -17,31 +17,43 @@ # It operates on the current, potentially unsaved buffer and does not create # or save any files. To revert a formatting, just undo. -import vim +import difflib +import json import subprocess +import sys +import vim # Change this to the full path if clang-format is not on the path. binary = 'clang-format' -# Change this to format according to other formatting styles (see -# clang-format -help) -style = 'LLVM' +# Change this to format according to other formatting styles. See the output of +# 'clang-format --help' for a list of supported styles. The default looks for +# a '.clang-format' or '_clang-format' file to indicate the style that should be +# used. +style = 'file' # Get the current text. buf = vim.current.buffer -text = "\n".join(buf) +text = '\n'.join(buf) # Determine range to format. -offset = int(vim.eval('line2byte(' + - str(vim.current.range.start + 1) + ')')) - 1 -length = int(vim.eval('line2byte(' + - str(vim.current.range.end + 2) + ')')) - offset - 2 +cursor = int(vim.eval('line2byte(line("."))+col(".")')) - 2 +lines = '%s:%s' % (vim.current.range.start + 1, vim.current.range.end + 1) + +# Avoid flashing an ugly, ugly cmd prompt on Windows when invoking clang-format. +startupinfo = None +if sys.platform.startswith('win32'): + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = subprocess.SW_HIDE # Call formatter. -p = subprocess.Popen([binary, '-offset', str(offset), '-length', str(length), - '-style', style], +command = [binary, '-lines', lines, '-style', style, '-cursor', str(cursor)] +if vim.current.buffer.name: + command.extend(['-assume-filename', vim.current.buffer.name]) +p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - stdin=subprocess.PIPE) + stdin=subprocess.PIPE, startupinfo=startupinfo) stdout, stderr = p.communicate(input=text) # If successful, replace buffer contents. @@ -56,10 +68,12 @@ if stderr: if not stdout: print ('No output from clang-format (crashed?).\n' + 'Please report to bugs.llvm.org.') -elif stdout != text: +else: lines = stdout.split('\n') - for i in range(min(len(buf), len(lines))): - buf[i] = lines[i] - for line in lines[len(buf):]: - buf.append(line) - del buf[len(lines):] + output = json.loads(lines[0]) + lines = lines[1:] + sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines) + for op in reversed(sequence.get_opcodes()): + if op[0] is not 'equal': + vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]] + vim.command('goto %d' % (output['Cursor'] + 1)) diff --git a/tools/clang-format/git-clang-format b/tools/clang-format/git-clang-format new file mode 100755 index 0000000..b0737ed --- /dev/null +++ b/tools/clang-format/git-clang-format @@ -0,0 +1,481 @@ +#!/usr/bin/python +# +#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +r""" +clang-format git integration +============================ + +This file provides a clang-format integration for git. Put it somewhere in your +path and ensure that it is executable. Then, "git clang-format" will invoke +clang-format on the changes in current files or a specific commit. + +For further details, run: +git clang-format -h + +Requires Python 2.7 +""" + +import argparse +import collections +import contextlib +import errno +import os +import re +import subprocess +import sys + +usage = 'git clang-format [OPTIONS] [<commit>] [--] [<file>...]' + +desc = ''' +Run clang-format on all lines that differ between the working directory +and <commit>, which defaults to HEAD. Changes are only applied to the working +directory. + +The following git-config settings set the default of the corresponding option: + clangFormat.binary + clangFormat.commit + clangFormat.extension + clangFormat.style +''' + +# Name of the temporary index file in which save the output of clang-format. +# This file is created within the .git directory. +temp_index_basename = 'clang-format-index' + + +Range = collections.namedtuple('Range', 'start, count') + + +def main(): + config = load_git_config() + + # In order to keep '--' yet allow options after positionals, we need to + # check for '--' ourselves. (Setting nargs='*' throws away the '--', while + # nargs=argparse.REMAINDER disallows options after positionals.) + argv = sys.argv[1:] + try: + idx = argv.index('--') + except ValueError: + dash_dash = [] + else: + dash_dash = argv[idx:] + argv = argv[:idx] + + default_extensions = ','.join([ + # From clang/lib/Frontend/FrontendOptions.cpp, all lower case + 'c', 'h', # C + 'm', # ObjC + 'mm', # ObjC++ + 'cc', 'cp', 'cpp', 'c++', 'cxx', 'hpp', # C++ + ]) + + p = argparse.ArgumentParser( + usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter, + description=desc) + p.add_argument('--binary', + default=config.get('clangformat.binary', 'clang-format'), + help='path to clang-format'), + p.add_argument('--commit', + default=config.get('clangformat.commit', 'HEAD'), + help='default commit to use if none is specified'), + p.add_argument('--diff', action='store_true', + help='print a diff instead of applying the changes') + p.add_argument('--extensions', + default=config.get('clangformat.extensions', + default_extensions), + help=('comma-separated list of file extensions to format, ' + 'excluding the period and case-insensitive')), + p.add_argument('-f', '--force', action='store_true', + help='allow changes to unstaged files') + p.add_argument('-p', '--patch', action='store_true', + help='select hunks interactively') + p.add_argument('-q', '--quiet', action='count', default=0, + help='print less information') + p.add_argument('--style', + default=config.get('clangformat.style', None), + help='passed to clang-format'), + p.add_argument('-v', '--verbose', action='count', default=0, + help='print extra information') + # We gather all the remaining positional arguments into 'args' since we need + # to use some heuristics to determine whether or not <commit> was present. + # However, to print pretty messages, we make use of metavar and help. + p.add_argument('args', nargs='*', metavar='<commit>', + help='revision from which to compute the diff') + p.add_argument('ignored', nargs='*', metavar='<file>...', + help='if specified, only consider differences in these files') + opts = p.parse_args(argv) + + opts.verbose -= opts.quiet + del opts.quiet + + commit, files = interpret_args(opts.args, dash_dash, opts.commit) + changed_lines = compute_diff_and_extract_lines(commit, files) + if opts.verbose >= 1: + ignored_files = set(changed_lines) + filter_by_extension(changed_lines, opts.extensions.lower().split(',')) + if opts.verbose >= 1: + ignored_files.difference_update(changed_lines) + if ignored_files: + print 'Ignoring changes in the following files (wrong extension):' + for filename in ignored_files: + print ' ', filename + if changed_lines: + print 'Running clang-format on the following files:' + for filename in changed_lines: + print ' ', filename + if not changed_lines: + print 'no modified files to format' + return + # The computed diff outputs absolute paths, so we must cd before accessing + # those files. + cd_to_toplevel() + old_tree = create_tree_from_workdir(changed_lines) + new_tree = run_clang_format_and_save_to_tree(changed_lines, + binary=opts.binary, + style=opts.style) + if opts.verbose >= 1: + print 'old tree:', old_tree + print 'new tree:', new_tree + if old_tree == new_tree: + if opts.verbose >= 0: + print 'clang-format did not modify any files' + elif opts.diff: + print_diff(old_tree, new_tree) + else: + changed_files = apply_changes(old_tree, new_tree, force=opts.force, + patch_mode=opts.patch) + if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1: + print 'changed files:' + for filename in changed_files: + print ' ', filename + + +def load_git_config(non_string_options=None): + """Return the git configuration as a dictionary. + + All options are assumed to be strings unless in `non_string_options`, in which + is a dictionary mapping option name (in lower case) to either "--bool" or + "--int".""" + if non_string_options is None: + non_string_options = {} + out = {} + for entry in run('git', 'config', '--list', '--null').split('\0'): + if entry: + name, value = entry.split('\n', 1) + if name in non_string_options: + value = run('git', 'config', non_string_options[name], name) + out[name] = value + return out + + +def interpret_args(args, dash_dash, default_commit): + """Interpret `args` as "[commit] [--] [files...]" and return (commit, files). + + It is assumed that "--" and everything that follows has been removed from + args and placed in `dash_dash`. + + If "--" is present (i.e., `dash_dash` is non-empty), the argument to its + left (if present) is taken as commit. Otherwise, the first argument is + checked if it is a commit or a file. If commit is not given, + `default_commit` is used.""" + if dash_dash: + if len(args) == 0: + commit = default_commit + elif len(args) > 1: + die('at most one commit allowed; %d given' % len(args)) + else: + commit = args[0] + object_type = get_object_type(commit) + if object_type not in ('commit', 'tag'): + if object_type is None: + die("'%s' is not a commit" % commit) + else: + die("'%s' is a %s, but a commit was expected" % (commit, object_type)) + files = dash_dash[1:] + elif args: + if disambiguate_revision(args[0]): + commit = args[0] + files = args[1:] + else: + commit = default_commit + files = args + else: + commit = default_commit + files = [] + return commit, files + + +def disambiguate_revision(value): + """Returns True if `value` is a revision, False if it is a file, or dies.""" + # If `value` is ambiguous (neither a commit nor a file), the following + # command will die with an appropriate error message. + run('git', 'rev-parse', value, verbose=False) + object_type = get_object_type(value) + if object_type is None: + return False + if object_type in ('commit', 'tag'): + return True + die('`%s` is a %s, but a commit or filename was expected' % + (value, object_type)) + + +def get_object_type(value): + """Returns a string description of an object's type, or None if it is not + a valid git object.""" + cmd = ['git', 'cat-file', '-t', value] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + if p.returncode != 0: + return None + return stdout.strip() + + +def compute_diff_and_extract_lines(commit, files): + """Calls compute_diff() followed by extract_lines().""" + diff_process = compute_diff(commit, files) + changed_lines = extract_lines(diff_process.stdout) + diff_process.stdout.close() + diff_process.wait() + if diff_process.returncode != 0: + # Assume error was already printed to stderr. + sys.exit(2) + return changed_lines + + +def compute_diff(commit, files): + """Return a subprocess object producing the diff from `commit`. + + The return value's `stdin` file object will produce a patch with the + differences between the working directory and `commit`, filtered on `files` + (if non-empty). Zero context lines are used in the patch.""" + cmd = ['git', 'diff-index', '-p', '-U0', commit, '--'] + cmd.extend(files) + p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + p.stdin.close() + return p + + +def extract_lines(patch_file): + """Extract the changed lines in `patch_file`. + + The return value is a dictionary mapping filename to a list of (start_line, + line_count) pairs. + + The input must have been produced with ``-U0``, meaning unidiff format with + zero lines of context. The return value is a dict mapping filename to a + list of line `Range`s.""" + matches = {} + for line in patch_file: + match = re.search(r'^\+\+\+\ [^/]+/(.*)', line) + if match: + filename = match.group(1).rstrip('\r\n') + match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(3): + line_count = int(match.group(3)) + if line_count > 0: + matches.setdefault(filename, []).append(Range(start_line, line_count)) + return matches + + +def filter_by_extension(dictionary, allowed_extensions): + """Delete every key in `dictionary` that doesn't have an allowed extension. + + `allowed_extensions` must be a collection of lowercase file extensions, + excluding the period.""" + allowed_extensions = frozenset(allowed_extensions) + for filename in dictionary.keys(): + base_ext = filename.rsplit('.', 1) + if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions: + del dictionary[filename] + + +def cd_to_toplevel(): + """Change to the top level of the git repository.""" + toplevel = run('git', 'rev-parse', '--show-toplevel') + os.chdir(toplevel) + + +def create_tree_from_workdir(filenames): + """Create a new git tree with the given files from the working directory. + + Returns the object ID (SHA-1) of the created tree.""" + return create_tree(filenames, '--stdin') + + +def run_clang_format_and_save_to_tree(changed_lines, binary='clang-format', + style=None): + """Run clang-format on each file and save the result to a git tree. + + Returns the object ID (SHA-1) of the created tree.""" + def index_info_generator(): + for filename, line_ranges in changed_lines.iteritems(): + mode = oct(os.stat(filename).st_mode) + blob_id = clang_format_to_blob(filename, line_ranges, binary=binary, + style=style) + yield '%s %s\t%s' % (mode, blob_id, filename) + return create_tree(index_info_generator(), '--index-info') + + +def create_tree(input_lines, mode): + """Create a tree object from the given input. + + If mode is '--stdin', it must be a list of filenames. If mode is + '--index-info' is must be a list of values suitable for "git update-index + --index-info", such as "<mode> <SP> <sha1> <TAB> <filename>". Any other mode + is invalid.""" + assert mode in ('--stdin', '--index-info') + cmd = ['git', 'update-index', '--add', '-z', mode] + with temporary_index_file(): + p = subprocess.Popen(cmd, stdin=subprocess.PIPE) + for line in input_lines: + p.stdin.write('%s\0' % line) + p.stdin.close() + if p.wait() != 0: + die('`%s` failed' % ' '.join(cmd)) + tree_id = run('git', 'write-tree') + return tree_id + + +def clang_format_to_blob(filename, line_ranges, binary='clang-format', + style=None): + """Run clang-format on the given file and save the result to a git blob. + + Returns the object ID (SHA-1) of the created blob.""" + clang_format_cmd = [binary, filename] + if style: + clang_format_cmd.extend(['-style='+style]) + clang_format_cmd.extend([ + '-lines=%s:%s' % (start_line, start_line+line_count-1) + for start_line, line_count in line_ranges]) + try: + clang_format = subprocess.Popen(clang_format_cmd, stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + except OSError as e: + if e.errno == errno.ENOENT: + die('cannot find executable "%s"' % binary) + else: + raise + clang_format.stdin.close() + hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin'] + hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout, + stdout=subprocess.PIPE) + clang_format.stdout.close() + stdout = hash_object.communicate()[0] + if hash_object.returncode != 0: + die('`%s` failed' % ' '.join(hash_object_cmd)) + if clang_format.wait() != 0: + die('`%s` failed' % ' '.join(clang_format_cmd)) + return stdout.rstrip('\r\n') + + +@contextlib.contextmanager +def temporary_index_file(tree=None): + """Context manager for setting GIT_INDEX_FILE to a temporary file and deleting + the file afterward.""" + index_path = create_temporary_index(tree) + old_index_path = os.environ.get('GIT_INDEX_FILE') + os.environ['GIT_INDEX_FILE'] = index_path + try: + yield + finally: + if old_index_path is None: + del os.environ['GIT_INDEX_FILE'] + else: + os.environ['GIT_INDEX_FILE'] = old_index_path + os.remove(index_path) + + +def create_temporary_index(tree=None): + """Create a temporary index file and return the created file's path. + + If `tree` is not None, use that as the tree to read in. Otherwise, an + empty index is created.""" + gitdir = run('git', 'rev-parse', '--git-dir') + path = os.path.join(gitdir, temp_index_basename) + if tree is None: + tree = '--empty' + run('git', 'read-tree', '--index-output='+path, tree) + return path + + +def print_diff(old_tree, new_tree): + """Print the diff between the two trees to stdout.""" + # We use the porcelain 'diff' and not plumbing 'diff-tree' because the output + # is expected to be viewed by the user, and only the former does nice things + # like color and pagination. + subprocess.check_call(['git', 'diff', old_tree, new_tree, '--']) + + +def apply_changes(old_tree, new_tree, force=False, patch_mode=False): + """Apply the changes in `new_tree` to the working directory. + + Bails if there are local changes in those files and not `force`. If + `patch_mode`, runs `git checkout --patch` to select hunks interactively.""" + changed_files = run('git', 'diff-tree', '-r', '-z', '--name-only', old_tree, + new_tree).rstrip('\0').split('\0') + if not force: + unstaged_files = run('git', 'diff-files', '--name-status', *changed_files) + if unstaged_files: + print >>sys.stderr, ('The following files would be modified but ' + 'have unstaged changes:') + print >>sys.stderr, unstaged_files + print >>sys.stderr, 'Please commit, stage, or stash them first.' + sys.exit(2) + if patch_mode: + # In patch mode, we could just as well create an index from the new tree + # and checkout from that, but then the user will be presented with a + # message saying "Discard ... from worktree". Instead, we use the old + # tree as the index and checkout from new_tree, which gives the slightly + # better message, "Apply ... to index and worktree". This is not quite + # right, since it won't be applied to the user's index, but oh well. + with temporary_index_file(old_tree): + subprocess.check_call(['git', 'checkout', '--patch', new_tree]) + index_tree = old_tree + else: + with temporary_index_file(new_tree): + run('git', 'checkout-index', '-a', '-f') + return changed_files + + +def run(*args, **kwargs): + stdin = kwargs.pop('stdin', '') + verbose = kwargs.pop('verbose', True) + strip = kwargs.pop('strip', True) + for name in kwargs: + raise TypeError("run() got an unexpected keyword argument '%s'" % name) + p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + stdin=subprocess.PIPE) + stdout, stderr = p.communicate(input=stdin) + if p.returncode == 0: + if stderr: + if verbose: + print >>sys.stderr, '`%s` printed to stderr:' % ' '.join(args) + print >>sys.stderr, stderr.rstrip() + if strip: + stdout = stdout.rstrip('\r\n') + return stdout + if verbose: + print >>sys.stderr, '`%s` returned %s' % (' '.join(args), p.returncode) + if stderr: + print >>sys.stderr, stderr.rstrip() + sys.exit(2) + + +def die(message): + print >>sys.stderr, 'error:', message + sys.exit(2) + + +if __name__ == '__main__': + main() diff --git a/tools/diagtool/DiagnosticNames.cpp b/tools/diagtool/DiagnosticNames.cpp index 31f3524..155c62d 100644 --- a/tools/diagtool/DiagnosticNames.cpp +++ b/tools/diagtool/DiagnosticNames.cpp @@ -29,8 +29,7 @@ llvm::ArrayRef<DiagnosticRecord> diagtool::getBuiltinDiagnosticsByName() { // out of sync easily? static const DiagnosticRecord BuiltinDiagnosticsByID[] = { #define DIAG(ENUM,CLASS,DEFAULT_MAPPING,DESC,GROUP, \ - SFINAE,ACCESS,NOWERROR,SHOWINSYSHEADER, \ - CATEGORY) \ + SFINAE,NOWERROR,SHOWINSYSHEADER,CATEGORY) \ { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) }, #include "clang/Basic/DiagnosticCommonKinds.inc" #include "clang/Basic/DiagnosticDriverKinds.inc" @@ -73,6 +72,26 @@ static const GroupRecord OptionTable[] = { #undef GET_DIAG_TABLE }; +llvm::StringRef GroupRecord::getName() const { + return StringRef(DiagGroupNames + NameOffset + 1, DiagGroupNames[NameOffset]); +} + +GroupRecord::subgroup_iterator GroupRecord::subgroup_begin() const { + return DiagSubGroups + SubGroups; +} + +GroupRecord::subgroup_iterator GroupRecord::subgroup_end() const { + return 0; +} + +GroupRecord::diagnostics_iterator GroupRecord::diagnostics_begin() const { + return DiagArrays + Members; +} + +GroupRecord::diagnostics_iterator GroupRecord::diagnostics_end() const { + return 0; +} + llvm::ArrayRef<GroupRecord> diagtool::getDiagnosticGroups() { return llvm::makeArrayRef(OptionTable); } diff --git a/tools/diagtool/DiagnosticNames.h b/tools/diagtool/DiagnosticNames.h index 9a73158..a3321fa 100644 --- a/tools/diagtool/DiagnosticNames.h +++ b/tools/diagtool/DiagnosticNames.h @@ -35,17 +35,11 @@ namespace diagtool { struct GroupRecord { - // Be safe with the size of 'NameLen' because we don't statically check if - // the size will fit in the field; the struct size won't decrease with a - // shorter type anyway. - size_t NameLen; - const char *NameStr; - const short *Members; - const short *SubGroups; - - llvm::StringRef getName() const { - return llvm::StringRef(NameStr, NameLen); - } + uint16_t NameOffset; + uint16_t Members; + uint16_t SubGroups; + + llvm::StringRef getName() const; template<typename RecordType> class group_iterator { @@ -90,23 +84,15 @@ namespace diagtool { }; typedef group_iterator<GroupRecord> subgroup_iterator; - subgroup_iterator subgroup_begin() const { - return SubGroups; - } - subgroup_iterator subgroup_end() const { - return 0; - } + subgroup_iterator subgroup_begin() const; + subgroup_iterator subgroup_end() const; typedef group_iterator<DiagnosticRecord> diagnostics_iterator; - diagnostics_iterator diagnostics_begin() const { - return Members; - } - diagnostics_iterator diagnostics_end() const { - return 0; - } + diagnostics_iterator diagnostics_begin() const; + diagnostics_iterator diagnostics_end() const; - bool operator<(const GroupRecord &Other) const { - return getName() < Other.getName(); + bool operator<(llvm::StringRef Other) const { + return getName() < Other; } }; diff --git a/tools/diagtool/Makefile b/tools/diagtool/Makefile index 94f9c76..d49e976 100644 --- a/tools/diagtool/Makefile +++ b/tools/diagtool/Makefile @@ -17,7 +17,7 @@ TOOL_NO_EXPORTS := 1 NO_INSTALL = 1 include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \ clangSema.a clangAnalysis.a clangEdit.a clangAST.a clangLex.a \ clangBasic.a diff --git a/tools/diagtool/TreeView.cpp b/tools/diagtool/TreeView.cpp index 6298179..fd548ef 100644 --- a/tools/diagtool/TreeView.cpp +++ b/tools/diagtool/TreeView.cpp @@ -94,9 +94,13 @@ static int showGroup(llvm::raw_ostream &out, StringRef RootGroup, bool FlagsOnly) { ArrayRef<GroupRecord> AllGroups = getDiagnosticGroups(); - GroupRecord Key = { RootGroup.size(), RootGroup.data(), 0, 0 }; + if (RootGroup.size() > UINT16_MAX) { + llvm::errs() << "No such diagnostic group exists\n"; + return 1; + } + const GroupRecord *Found = - std::lower_bound(AllGroups.begin(), AllGroups.end(), Key); + std::lower_bound(AllGroups.begin(), AllGroups.end(), RootGroup); if (Found == AllGroups.end() || Found->getName() != RootGroup) { llvm::errs() << "No such diagnostic group exists\n"; diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 97ac7a4..c94bc77 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -29,16 +29,31 @@ target_link_libraries(clang clangLex clangParse clangEdit - clangARCMigrate - clangRewriteCore - clangRewriteFrontend clangSema clangSerialization - clangStaticAnalyzerFrontend - clangStaticAnalyzerCheckers - clangStaticAnalyzerCore ) +if(CLANG_ENABLE_STATIC_ANALYZER) + target_link_libraries(clang + clangStaticAnalyzerFrontend + clangStaticAnalyzerCheckers + clangStaticAnalyzerCore + ) +endif() + +if(CLANG_ENABLE_ARCMT) + target_link_libraries(clang + clangARCMigrate + ) +endif() + +if(CLANG_ENABLE_REWRITER) + target_link_libraries(clang + clangRewriteCore + clangRewriteFrontend + ) +endif() + set_target_properties(clang PROPERTIES VERSION ${CLANG_EXECUTABLE_VERSION}) set_target_properties(clang PROPERTIES ENABLE_EXPORTS 1) @@ -50,19 +65,59 @@ if(UNIX) set(clang_binary "clang${CMAKE_EXECUTABLE_SUFFIX}") else() set(CLANGXX_LINK_OR_COPY copy) - set(clang_binary "${LLVM_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/clang${CMAKE_EXECUTABLE_SUFFIX}") + set(clang_binary "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/clang${CMAKE_EXECUTABLE_SUFFIX}") endif() # Create the clang++ symlink in the build directory. -set(clang_pp "${LLVM_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/clang++${CMAKE_EXECUTABLE_SUFFIX}") +set(clang_pp "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/clang++${CMAKE_EXECUTABLE_SUFFIX}") add_custom_command(TARGET clang POST_BUILD COMMAND ${CMAKE_COMMAND} -E ${CLANGXX_LINK_OR_COPY} "${clang_binary}" "${clang_pp}") set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${clang_pp}) +# Create the clang-cl symlink in the build directory. +set(clang_cl "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/clang-cl${CMAKE_EXECUTABLE_SUFFIX}") +add_custom_command(TARGET clang POST_BUILD + COMMAND ${CMAKE_COMMAND} -E ${CLANGXX_LINK_OR_COPY} "${clang_binary}" "${clang_cl}") + +set_property(DIRECTORY APPEND + PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${clang_cl}) + install(TARGETS clang RUNTIME DESTINATION bin) -# Create the clang++ symlink at installation time. +# Create the clang++ and clang-cl symlinks at installation time. install(SCRIPT clang_symlink.cmake -DCMAKE_INSTALL_PREFIX=\"${CMAKE_INSTALL_PREFIX}\") + +# Configure plist creation for OS X. +set (TOOL_INFO_PLIST "Info.plist" CACHE STRING "Plist name") +if (APPLE) + if (CLANG_VENDOR) + set(TOOL_INFO_NAME "${CLANG_VENDOR} clang") + else() + set(TOOL_INFO_NAME "clang") + endif() + + set(TOOL_INFO_UTI "${CLANG_VENDOR_UTI}") + set(TOOL_INFO_VERSION "${CLANG_VERSION}") + if (LLVM_SUBMIT_VERSION) + set(TOOL_INFO_BUILD_VERSION + "${LLVM_SUBMIT_VERSION}.${LLVM_SUBMIT_SUBVERSION}") + endif() + + set(TOOL_INFO_PLIST_OUT "${CMAKE_CURRENT_BINARY_DIR}/${TOOL_INFO_PLIST}") + target_link_libraries(clang + "-Wl,-sectcreate,__TEXT,__info_plist,${TOOL_INFO_PLIST_OUT}") + configure_file("${TOOL_INFO_PLIST}.in" "${TOOL_INFO_PLIST_OUT}" @ONLY) + + set(TOOL_INFO_UTI) + set(TOOL_INFO_NAME) + set(TOOL_INFO_VERSION) + set(TOOL_INFO_BUILD_VERSION) +endif() + +if(CLANG_ORDER_FILE) + target_link_libraries(clang "-Wl,-order_file,${CLANG_ORDER_FILE}") +endif() + diff --git a/tools/driver/Makefile b/tools/driver/Makefile index cdf3b52..f7a9f8f 100644 --- a/tools/driver/Makefile +++ b/tools/driver/Makefile @@ -30,7 +30,7 @@ TOOL_INFO_PLIST := Info.plist include $(CLANG_LEVEL)/../../Makefile.config LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader bitwriter codegen \ - instrumentation ipo irreader linker selectiondag + instrumentation ipo irreader linker selectiondag option USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ clangSerialization.a clangCodeGen.a clangParse.a clangSema.a diff --git a/tools/driver/cc1_main.cpp b/tools/driver/cc1_main.cpp index 35cf5b8..5b3b5ad 100644 --- a/tools/driver/cc1_main.cpp +++ b/tools/driver/cc1_main.cpp @@ -13,10 +13,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/Driver/Arg.h" -#include "clang/Driver/ArgList.h" +#include "llvm/Option/Arg.h" #include "clang/Driver/DriverDiagnostic.h" -#include "clang/Driver/OptTable.h" #include "clang/Driver/Options.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" @@ -26,6 +24,8 @@ #include "clang/FrontendTool/Utils.h" #include "llvm/ADT/Statistic.h" #include "llvm/LinkAllPasses.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Signals.h" @@ -34,6 +34,7 @@ #include "llvm/Support/raw_ostream.h" #include <cstdio> using namespace clang; +using namespace llvm::opt; //===----------------------------------------------------------------------===// // Main driver diff --git a/tools/driver/cc1as_main.cpp b/tools/driver/cc1as_main.cpp index 232ea2f..31cd236 100644 --- a/tools/driver/cc1as_main.cpp +++ b/tools/driver/cc1as_main.cpp @@ -14,14 +14,12 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" -#include "clang/Driver/Arg.h" -#include "clang/Driver/ArgList.h" #include "clang/Driver/CC1AsOptions.h" #include "clang/Driver/DriverDiagnostic.h" -#include "clang/Driver/OptTable.h" #include "clang/Driver/Options.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" @@ -37,8 +35,12 @@ #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCTargetAsmParser.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/Host.h" #include "llvm/Support/ManagedStatic.h" @@ -55,6 +57,7 @@ using namespace clang; using namespace clang::driver; using namespace llvm; +using namespace llvm::opt; namespace { @@ -181,7 +184,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, // Language Options Opts.IncludePaths = Args->getAllArgValues(OPT_I); Opts.NoInitialTextSection = Args->hasArg(OPT_n); - Opts.SaveTemporaryLabels = Args->hasArg(OPT_L); + Opts.SaveTemporaryLabels = Args->hasArg(OPT_msave_temp_labels); Opts.GenDwarfForAssembly = Args->hasArg(OPT_g); Opts.DwarfDebugFlags = Args->getLastArgValue(OPT_dwarf_debug_flags); Opts.DwarfDebugProducer = Args->getLastArgValue(OPT_dwarf_debug_producer); @@ -203,8 +206,6 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, } } Opts.LLVMArgs = Args->getAllArgValues(OPT_mllvm); - if (Args->hasArg(OPT_fatal_warnings)) - Opts.LLVMArgs.push_back("-fatal-assembler-warnings"); Opts.OutputPath = Args->getLastArgValue(OPT_o); if (Arg *A = Args->getLastArg(OPT_filetype)) { StringRef Name = A->getValue(); @@ -224,14 +225,14 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, Opts.ShowVersion = Args->hasArg(OPT_version); // Transliterate Options - Opts.OutputAsmVariant = Args->getLastArgIntValue(OPT_output_asm_variant, - 0, Diags); + Opts.OutputAsmVariant = + getLastArgIntValue(*Args.get(), OPT_output_asm_variant, 0, Diags); Opts.ShowEncoding = Args->hasArg(OPT_show_encoding); Opts.ShowInst = Args->hasArg(OPT_show_inst); // Assemble Options - Opts.RelaxAll = Args->hasArg(OPT_relax_all); - Opts.NoExecStack = Args->hasArg(OPT_no_exec_stack); + Opts.RelaxAll = Args->hasArg(OPT_mrelax_all); + Opts.NoExecStack = Args->hasArg(OPT_mno_exec_stack); return Success; } @@ -245,12 +246,12 @@ static formatted_raw_ostream *GetOutputStream(AssemblerInvocation &Opts, // Make sure that the Out file gets unlinked from the disk if we get a // SIGINT. if (Opts.OutputPath != "-") - sys::RemoveFileOnSignal(sys::Path(Opts.OutputPath)); + sys::RemoveFileOnSignal(Opts.OutputPath); std::string Error; raw_fd_ostream *Out = - new raw_fd_ostream(Opts.OutputPath.c_str(), Error, - (Binary ? raw_fd_ostream::F_Binary : 0)); + new raw_fd_ostream(Opts.OutputPath.c_str(), Error, + (Binary ? sys::fs::F_Binary : sys::fs::F_None)); if (!Error.empty()) { Diags.Report(diag::err_fe_unable_to_open_output) << Opts.OutputPath << Error; @@ -287,12 +288,12 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, // it later. SrcMgr.setIncludeDirs(Opts.IncludePaths); - OwningPtr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(Opts.Triple)); - assert(MAI && "Unable to create target asm info!"); - OwningPtr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(Opts.Triple)); assert(MRI && "Unable to create target register info!"); + OwningPtr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, Opts.Triple)); + assert(MAI && "Unable to create target asm info!"); + bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj; formatted_raw_ostream *Out = GetOutputStream(Opts, Diags, IsBinary); if (!Out) @@ -301,7 +302,7 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and // MCObjectFileInfo needs a MCContext reference in order to initialize itself. OwningPtr<MCObjectFileInfo> MOFI(new MCObjectFileInfo()); - MCContext Ctx(*MAI, *MRI, MOFI.get(), &SrcMgr); + MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr); // FIXME: Assembler behavior can change with -static. MOFI->InitMCObjectFileInfo(Opts.Triple, Reloc::Default, CodeModel::Default, Ctx); @@ -341,7 +342,7 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, MCAsmBackend *MAB = 0; if (Opts.ShowEncoding) { CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, *STI, Ctx); - MAB = TheTarget->createMCAsmBackend(Opts.Triple, Opts.CPU); + MAB = TheTarget->createMCAsmBackend(*MRI, Opts.Triple, Opts.CPU); } Str.reset(TheTarget->createAsmStreamer(Ctx, *Out, /*asmverbose*/true, /*useLoc*/ true, @@ -355,7 +356,8 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, assert(Opts.OutputType == AssemblerInvocation::FT_Obj && "Invalid file type!"); MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, *STI, Ctx); - MCAsmBackend *MAB = TheTarget->createMCAsmBackend(Opts.Triple, Opts.CPU); + MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*MRI, Opts.Triple, + Opts.CPU); Str.reset(TheTarget->createMCObjectStreamer(Opts.Triple, Ctx, *MAB, *Out, CE, Opts.RelaxAll, Opts.NoExecStack)); @@ -364,7 +366,7 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, OwningPtr<MCAsmParser> Parser(createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI)); - OwningPtr<MCTargetAsmParser> TAP(TheTarget->createMCAsmParser(*STI, *Parser)); + OwningPtr<MCTargetAsmParser> TAP(TheTarget->createMCAsmParser(*STI, *Parser, *MCII)); if (!TAP) { Diags.Report(diag::err_target_unknown_triple) << Opts.Triple; return false; @@ -379,7 +381,7 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, // Delete output on errors. if (!Success && Opts.OutputPath != "-") - sys::Path(Opts.OutputPath).eraseFromDisk(); + sys::fs::remove(Opts.OutputPath); return Success; } @@ -426,7 +428,7 @@ int cc1as_main(const char **ArgBegin, const char **ArgEnd, // Honor -help. if (Asm.ShowHelp) { - OwningPtr<driver::OptTable> Opts(driver::createCC1AsOptTable()); + OwningPtr<OptTable> Opts(driver::createCC1AsOptTable()); Opts->PrintHelp(llvm::outs(), "clang -cc1as", "Clang Integrated Assembler"); return 0; } diff --git a/tools/driver/clang_symlink.cmake b/tools/driver/clang_symlink.cmake index c7341cb..c012595 100644 --- a/tools/driver/clang_symlink.cmake +++ b/tools/driver/clang_symlink.cmake @@ -19,9 +19,25 @@ endif() set(bindir "${CLANGXX_DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/") set(clang "clang${EXECUTABLE_SUFFIX}") set(clangxx "clang++${EXECUTABLE_SUFFIX}") +set(clang_cl "clang-cl${EXECUTABLE_SUFFIX}") +set(cl "cl${EXECUTABLE_SUFFIX}") message("Creating clang++ executable based on ${clang}") execute_process( COMMAND "${CMAKE_COMMAND}" -E ${CLANGXX_LINK_OR_COPY} "${clang}" "${clangxx}" WORKING_DIRECTORY "${bindir}") + +message("Creating clang-cl executable based on ${clang}") + +execute_process( + COMMAND "${CMAKE_COMMAND}" -E ${CLANGXX_LINK_OR_COPY} "${clang}" "${clang_cl}" + WORKING_DIRECTORY "${bindir}") + +if (WIN32) + message("Creating cl executable based on ${clang}") + + execute_process( + COMMAND "${CMAKE_COMMAND}" -E ${CLANGXX_LINK_OR_COPY} "${clang}" "../msbuild-bin/${cl}" + WORKING_DIRECTORY "${bindir}") +endif() diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 4c40da3..3a6a09b 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -14,12 +14,9 @@ #include "clang/Basic/CharInfo.h" #include "clang/Basic/DiagnosticOptions.h" -#include "clang/Driver/ArgList.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" -#include "clang/Driver/OptTable.h" -#include "clang/Driver/Option.h" #include "clang/Driver/Options.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/TextDiagnosticPrinter.h" @@ -28,6 +25,11 @@ #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -35,6 +37,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/Regex.h" #include "llvm/Support/Signals.h" @@ -45,15 +48,16 @@ #include "llvm/Support/system_error.h" using namespace clang; using namespace clang::driver; +using namespace llvm::opt; -llvm::sys::Path GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) { +std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) { if (!CanonicalPrefixes) - return llvm::sys::Path(Argv0); + return Argv0; // This just needs to be some symbol in the binary; C++ doesn't // allow taking the address of ::main however. void *P = (void*) (intptr_t) GetExecutablePath; - return llvm::sys::Path::GetMainExecutable(Argv0, P); + return llvm::sys::fs::getMainExecutable(Argv0, P); } static const char *SaveStringInSet(std::set<std::string> &SavedStrings, @@ -187,78 +191,6 @@ extern int cc1_main(const char **ArgBegin, const char **ArgEnd, extern int cc1as_main(const char **ArgBegin, const char **ArgEnd, const char *Argv0, void *MainAddr); -static void ExpandArgsFromBuf(const char *Arg, - SmallVectorImpl<const char*> &ArgVector, - std::set<std::string> &SavedStrings) { - const char *FName = Arg + 1; - OwningPtr<llvm::MemoryBuffer> MemBuf; - if (llvm::MemoryBuffer::getFile(FName, MemBuf)) { - ArgVector.push_back(SaveStringInSet(SavedStrings, Arg)); - return; - } - - const char *Buf = MemBuf->getBufferStart(); - char InQuote = ' '; - std::string CurArg; - - for (const char *P = Buf; ; ++P) { - if (*P == '\0' || (isWhitespace(*P) && InQuote == ' ')) { - if (!CurArg.empty()) { - - if (CurArg[0] != '@') { - ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg)); - } else { - ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings); - } - - CurArg = ""; - } - if (*P == '\0') - break; - else - continue; - } - - if (isWhitespace(*P)) { - if (InQuote != ' ') - CurArg.push_back(*P); - continue; - } - - if (*P == '"' || *P == '\'') { - if (InQuote == *P) - InQuote = ' '; - else if (InQuote == ' ') - InQuote = *P; - else - CurArg.push_back(*P); - continue; - } - - if (*P == '\\') { - ++P; - if (*P != '\0') - CurArg.push_back(*P); - continue; - } - CurArg.push_back(*P); - } -} - -static void ExpandArgv(int argc, const char **argv, - SmallVectorImpl<const char*> &ArgVector, - std::set<std::string> &SavedStrings) { - for (int i = 0; i < argc; ++i) { - const char *Arg = argv[i]; - if (Arg[0] != '@') { - ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg))); - continue; - } - - ExpandArgsFromBuf(Arg, ArgVector, SavedStrings); - } -} - static void ParseProgName(SmallVectorImpl<const char *> &ArgVector, std::set<std::string> &SavedStrings, Driver &TheDriver) @@ -279,21 +211,24 @@ static void ParseProgName(SmallVectorImpl<const char *> &ArgVector, // is gets added via -target as implicit first argument. static const struct { const char *Suffix; - bool IsCXX; - bool IsCPP; + const char *ModeFlag; } suffixes [] = { - { "clang", false, false }, - { "clang++", true, false }, - { "clang-c++", true, false }, - { "clang-cc", false, false }, - { "clang-cpp", false, true }, - { "clang-g++", true, false }, - { "clang-gcc", false, false }, - { "cc", false, false }, - { "cpp", false, true }, - { "++", true, false }, + { "clang", 0 }, + { "clang++", "--driver-mode=g++" }, + { "clang-c++", "--driver-mode=g++" }, + { "clang-cc", 0 }, + { "clang-cpp", "--driver-mode=cpp" }, + { "clang-g++", "--driver-mode=g++" }, + { "clang-gcc", 0 }, + { "clang-cl", "--driver-mode=cl" }, + { "cc", 0 }, + { "cpp", "--driver-mode=cpp" }, + { "cl" , "--driver-mode=cl" }, + { "++", "--driver-mode=g++" }, }; std::string ProgName(llvm::sys::path::stem(ArgVector[0])); + std::transform(ProgName.begin(), ProgName.end(), ProgName.begin(), + toLowercase); StringRef ProgNameRef(ProgName); StringRef Prefix; @@ -304,10 +239,11 @@ static void ParseProgName(SmallVectorImpl<const char *> &ArgVector, for (i = 0; i < sizeof(suffixes) / sizeof(suffixes[0]); ++i) { if (ProgNameRef.endswith(suffixes[i].Suffix)) { FoundMatch = true; - if (suffixes[i].IsCXX) - TheDriver.CCCIsCXX = true; - if (suffixes[i].IsCPP) - TheDriver.CCCIsCPP = true; + SmallVectorImpl<const char *>::iterator it = ArgVector.begin(); + if (it != ArgVector.end()) + ++it; + if (suffixes[i].ModeFlag) + ArgVector.insert(it, suffixes[i].ModeFlag); break; } } @@ -334,20 +270,41 @@ static void ParseProgName(SmallVectorImpl<const char *> &ArgVector, SmallVectorImpl<const char *>::iterator it = ArgVector.begin(); if (it != ArgVector.end()) ++it; - ArgVector.insert(it, SaveStringInSet(SavedStrings, Prefix)); - ArgVector.insert(it, - SaveStringInSet(SavedStrings, std::string("-target"))); + const char* Strings[] = + { SaveStringInSet(SavedStrings, std::string("-target")), + SaveStringInSet(SavedStrings, Prefix) }; + ArgVector.insert(it, Strings, Strings + llvm::array_lengthof(Strings)); } } +namespace { + class StringSetSaver : public llvm::cl::StringSaver { + public: + StringSetSaver(std::set<std::string> &Storage) : Storage(Storage) {} + const char *SaveString(const char *Str) LLVM_OVERRIDE { + return SaveStringInSet(Storage, Str); + } + private: + std::set<std::string> &Storage; + }; +} + int main(int argc_, const char **argv_) { llvm::sys::PrintStackTraceOnErrorSignal(); llvm::PrettyStackTraceProgram X(argc_, argv_); - std::set<std::string> SavedStrings; - SmallVector<const char*, 256> argv; + SmallVector<const char *, 256> argv; + llvm::SpecificBumpPtrAllocator<char> ArgAllocator; + llvm::error_code EC = llvm::sys::Process::GetArgumentVector( + argv, llvm::ArrayRef<const char *>(argv_, argc_), ArgAllocator); + if (EC) { + llvm::errs() << "error: couldn't get arguments: " << EC.message() << '\n'; + return 1; + } - ExpandArgv(argc_, argv_, argv, SavedStrings); + std::set<std::string> SavedStrings; + StringSetSaver Saver(SavedStrings); + llvm::cl::ExpandResponseFiles(Saver, llvm::cl::TokenizeGNUCommandLine, argv); // Handle -cc1 integrated tools. if (argv.size() > 1 && StringRef(argv[1]).startswith("-cc1")) { @@ -378,36 +335,17 @@ int main(int argc_, const char **argv_) { if (const char *OverrideStr = ::getenv("QA_OVERRIDE_GCC3_OPTIONS")) { // FIXME: Driver shouldn't take extra initial argument. ApplyQAOverride(argv, OverrideStr, SavedStrings); - } else if (const char *Cur = ::getenv("CCC_ADD_ARGS")) { - // FIXME: Driver shouldn't take extra initial argument. - std::vector<const char*> ExtraArgs; - - for (;;) { - const char *Next = strchr(Cur, ','); - - if (Next) { - ExtraArgs.push_back(SaveStringInSet(SavedStrings, - std::string(Cur, Next))); - Cur = Next + 1; - } else { - if (*Cur != '\0') - ExtraArgs.push_back(SaveStringInSet(SavedStrings, Cur)); - break; - } - } - - argv.insert(&argv[1], ExtraArgs.begin(), ExtraArgs.end()); } - llvm::sys::Path Path = GetExecutablePath(argv[0], CanonicalPrefixes); + std::string Path = GetExecutablePath(argv[0], CanonicalPrefixes); IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions; { - // Note that ParseDiagnosticArgs() uses the cc1 option table. - OwningPtr<OptTable> CC1Opts(createDriverOptTable()); + OwningPtr<OptTable> Opts(createDriverOptTable()); unsigned MissingArgIndex, MissingArgCount; - OwningPtr<InputArgList> Args(CC1Opts->ParseArgs(argv.begin()+1, argv.end(), - MissingArgIndex, MissingArgCount)); + OwningPtr<InputArgList> Args(Opts->ParseArgs(argv.begin()+1, argv.end(), + MissingArgIndex, + MissingArgCount)); // We ignore MissingArgCount and the return value of ParseDiagnosticArgs. // Any errors that would be diagnosed here will also be diagnosed later, // when the DiagnosticsEngine actually exists. @@ -417,14 +355,20 @@ int main(int argc_, const char **argv_) { // DiagnosticOptions instance. TextDiagnosticPrinter *DiagClient = new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); - DiagClient->setPrefix(llvm::sys::path::filename(Path.str())); + + // If the clang binary happens to be named cl.exe for compatibility reasons, + // use clang-cl.exe as the prefix to avoid confusion between clang and MSVC. + StringRef ExeBasename(llvm::sys::path::filename(Path)); + if (ExeBasename.equals_lower("cl.exe")) + ExeBasename = "clang-cl.exe"; + DiagClient->setPrefix(ExeBasename); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient); ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false); - Driver TheDriver(Path.str(), llvm::sys::getDefaultTargetTriple(), - "a.out", Diags); + Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), "a.out", Diags); // Attempt to find the original path used to invoke the driver, to determine // the installed path. We do this manually, because we want to support that @@ -434,10 +378,10 @@ int main(int argc_, const char **argv_) { // Do a PATH lookup, if there are no directory components. if (llvm::sys::path::filename(InstalledPath) == InstalledPath) { - llvm::sys::Path Tmp = llvm::sys::Program::FindProgramByName( + std::string Tmp = llvm::sys::FindProgramByName( llvm::sys::path::filename(InstalledPath.str())); if (!Tmp.empty()) - InstalledPath = Tmp.str(); + InstalledPath = Tmp; } llvm::sys::fs::make_absolute(InstalledPath); InstalledPath = llvm::sys::path::parent_path(InstalledPath); diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index a43c1ab..f53e5c1 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -22,13 +22,14 @@ #include "CXTranslationUnit.h" #include "CXType.h" #include "CursorVisitor.h" -#include "SimpleFormatContext.h" +#include "clang/AST/Attr.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/Version.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Index/CommentToXML.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/PreprocessingRecord.h" @@ -42,7 +43,6 @@ #include "llvm/Support/Format.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Mutex.h" -#include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Program.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/Signals.h" @@ -68,8 +68,7 @@ CXTranslationUnit cxtu::MakeCXTranslationUnit(CIndexer *CIdx, ASTUnit *AU) { D->StringPool = new cxstring::CXStringPool(); D->Diagnostics = 0; D->OverridenCursorsPool = createOverridenCXCursorsPool(); - D->FormatContext = 0; - D->FormatInMemoryUniqueId = 0; + D->CommentToXML = 0; return D; } @@ -308,8 +307,8 @@ bool CursorVisitor::visitDeclsFromFileRegion(FileID File, bool VisitedAtLeastOnce = false; DeclContext *CurDC = 0; - SmallVector<Decl *, 16>::iterator DIt = Decls.begin(); - for (SmallVector<Decl *, 16>::iterator DE = Decls.end(); DIt != DE; ++DIt) { + SmallVectorImpl<Decl *>::iterator DIt = Decls.begin(); + for (SmallVectorImpl<Decl *>::iterator DE = Decls.end(); DIt != DE; ++DIt) { Decl *D = *DIt; if (D->getSourceRange().isInvalid()) continue; @@ -426,6 +425,9 @@ bool CursorVisitor::visitPreprocessedEntities(InputIterator First, continue; PreprocessedEntity *PPE = *First; + if (!PPE) + continue; + if (MacroExpansion *ME = dyn_cast<MacroExpansion>(PPE)) { if (Visit(MakeMacroExpansionCursor(ME, TU))) return true; @@ -528,9 +530,10 @@ bool CursorVisitor::VisitChildren(CXCursor Cursor) { if (Cursor.kind == CXCursor_IBOutletCollectionAttr) { const IBOutletCollectionAttr *A = cast<IBOutletCollectionAttr>(cxcursor::getCursorAttr(Cursor)); - if (const ObjCInterfaceType *InterT = A->getInterface()->getAs<ObjCInterfaceType>()) - return Visit(cxcursor::MakeCursorObjCClassRef(InterT->getInterface(), - A->getInterfaceLoc(), TU)); + if (const ObjCObjectType *ObjT = A->getInterface()->getAs<ObjCObjectType>()) + return Visit(cxcursor::MakeCursorObjCClassRef( + ObjT->getInterface(), + A->getInterfaceLoc()->getTypeLoc().getLocStart(), TU)); } // If pointing inside a macro definition, check if the token is an identifier @@ -698,8 +701,9 @@ bool CursorVisitor::VisitClassTemplatePartialSpecializationDecl( return true; // Visit the partial specialization arguments. - const TemplateArgumentLoc *TemplateArgs = D->getTemplateArgsAsWritten(); - for (unsigned I = 0, N = D->getNumTemplateArgsAsWritten(); I != N; ++I) + const ASTTemplateArgumentListInfo *Info = D->getTemplateArgsAsWritten(); + const TemplateArgumentLoc *TemplateArgs = Info->getTemplateArgs(); + for (unsigned I = 0, N = Info->NumTemplateArgs; I != N; ++I) if (VisitTemplateArgumentLoc(TemplateArgs[I])) return true; @@ -743,18 +747,9 @@ bool CursorVisitor::VisitDeclaratorDecl(DeclaratorDecl *DD) { } /// \brief Compare two base or member initializers based on their source order. -static int CompareCXXCtorInitializers(const void* Xp, const void *Yp) { - CXXCtorInitializer const * const *X - = static_cast<CXXCtorInitializer const * const *>(Xp); - CXXCtorInitializer const * const *Y - = static_cast<CXXCtorInitializer const * const *>(Yp); - - if ((*X)->getSourceOrder() < (*Y)->getSourceOrder()) - return -1; - else if ((*X)->getSourceOrder() > (*Y)->getSourceOrder()) - return 1; - else - return 0; +static int CompareCXXCtorInitializers(CXXCtorInitializer *const *X, + CXXCtorInitializer *const *Y) { + return (*X)->getSourceOrder() - (*Y)->getSourceOrder(); } bool CursorVisitor::VisitFunctionDecl(FunctionDecl *ND) { @@ -1543,6 +1538,10 @@ bool CursorVisitor::VisitArrayTypeLoc(ArrayTypeLoc TL) { return false; } +bool CursorVisitor::VisitDecayedTypeLoc(DecayedTypeLoc TL) { + return Visit(TL.getOriginalLoc()); +} + bool CursorVisitor::VisitTemplateSpecializationTypeLoc( TemplateSpecializationTypeLoc TL) { // Visit the template name. @@ -1795,6 +1794,7 @@ public: } }; class EnqueueVisitor : public ConstStmtVisitor<EnqueueVisitor, void> { + friend class OMPClauseEnqueue; VisitorWorkList &WL; CXCursor Parent; public: @@ -1846,6 +1846,8 @@ public: void VisitPseudoObjectExpr(const PseudoObjectExpr *E); void VisitOpaqueValueExpr(const OpaqueValueExpr *E); void VisitLambdaExpr(const LambdaExpr *E); + void VisitOMPExecutableDirective(const OMPExecutableDirective *D); + void VisitOMPParallelDirective(const OMPParallelDirective *D); private: void AddDeclarationNameInfo(const Stmt *S); @@ -1856,6 +1858,7 @@ private: void AddDecl(const Decl *D, bool isFirst = true); void AddTypeLoc(TypeSourceInfo *TI); void EnqueueChildren(const Stmt *S); + void EnqueueChildren(const OMPClause *S); }; } // end anonyous namespace @@ -1904,6 +1907,52 @@ void EnqueueVisitor::EnqueueChildren(const Stmt *S) { VisitorWorkList::iterator I = WL.begin() + size, E = WL.end(); std::reverse(I, E); } +namespace { +class OMPClauseEnqueue : public ConstOMPClauseVisitor<OMPClauseEnqueue> { + EnqueueVisitor *Visitor; + /// \brief Process clauses with list of variables. + template <typename T> + void VisitOMPClauseList(T *Node); +public: + OMPClauseEnqueue(EnqueueVisitor *Visitor) : Visitor(Visitor) { } +#define OPENMP_CLAUSE(Name, Class) \ + void Visit##Class(const Class *C); +#include "clang/Basic/OpenMPKinds.def" +}; + +void OMPClauseEnqueue::VisitOMPDefaultClause(const OMPDefaultClause *C) { } + +template<typename T> +void OMPClauseEnqueue::VisitOMPClauseList(T *Node) { + for (typename T::varlist_const_iterator I = Node->varlist_begin(), + E = Node->varlist_end(); + I != E; ++I) + Visitor->AddStmt(*I); +} + +void OMPClauseEnqueue::VisitOMPPrivateClause(const OMPPrivateClause *C) { + VisitOMPClauseList(C); +} +void OMPClauseEnqueue::VisitOMPFirstprivateClause( + const OMPFirstprivateClause *C) { + VisitOMPClauseList(C); +} +void OMPClauseEnqueue::VisitOMPSharedClause(const OMPSharedClause *C) { + VisitOMPClauseList(C); +} +} + +void EnqueueVisitor::EnqueueChildren(const OMPClause *S) { + unsigned size = WL.size(); + OMPClauseEnqueue Visitor(this); + Visitor.Visit(S); + if (size == WL.size()) + return; + // Now reverse the entries we just added. This will match the DFS + // ordering performed by the worklist. + VisitorWorkList::iterator I = WL.begin() + size, E = WL.end(); + std::reverse(I, E); +} void EnqueueVisitor::VisitAddrLabelExpr(const AddrLabelExpr *E) { WL.push_back(LabelRefVisit(E->getLabel(), E->getLabelLoc(), Parent)); } @@ -2027,7 +2076,6 @@ void EnqueueVisitor::VisitDeclStmt(const DeclStmt *S) { } void EnqueueVisitor::VisitDesignatedInitExpr(const DesignatedInitExpr *E) { AddStmt(E->getInit()); - typedef DesignatedInitExpr::Designator Designator; for (DesignatedInitExpr::const_reverse_designators_iterator D = E->designators_rbegin(), DEnd = E->designators_rend(); D != DEnd; ++D) { @@ -2182,6 +2230,19 @@ void EnqueueVisitor::VisitPseudoObjectExpr(const PseudoObjectExpr *E) { Visit(E->getSyntacticForm()); } +void EnqueueVisitor::VisitOMPExecutableDirective( + const OMPExecutableDirective *D) { + EnqueueChildren(D); + for (ArrayRef<OMPClause *>::iterator I = D->clauses().begin(), + E = D->clauses().end(); + I != E; ++I) + EnqueueChildren(*I); +} + +void EnqueueVisitor::VisitOMPParallelDirective(const OMPParallelDirective *D) { + VisitOMPExecutableDirective(D); +} + void CursorVisitor::EnqueueWorkList(VisitorWorkList &WL, const Stmt *S) { EnqueueVisitor(WL, MakeCXCursor(S, StmtParent, TU,RegionOfInterest)).Visit(S); } @@ -2198,8 +2259,7 @@ bool CursorVisitor::IsInRegionOfInterest(CXCursor C) { bool CursorVisitor::RunVisitorWorkList(VisitorWorkList &WL) { while (!WL.empty()) { // Dequeue the worklist item. - VisitorJob LI = WL.back(); - WL.pop_back(); + VisitorJob LI = WL.pop_back_val(); // Set the Parent field, then back to its old value once we're done. SetParentRAII SetParent(Parent, StmtParent, LI.getParent()); @@ -2363,9 +2423,10 @@ bool CursorVisitor::RunVisitorWorkList(VisitorWorkList &WL) { for (LambdaExpr::capture_iterator C = E->explicit_capture_begin(), CEnd = E->explicit_capture_end(); C != CEnd; ++C) { - if (C->capturesThis()) + // FIXME: Lambda init-captures. + if (!C->capturesVariable()) continue; - + if (Visit(MakeCursorVariableRef(C->getCapturedVar(), C->getLocation(), TU))) @@ -2481,10 +2542,6 @@ static void fatal_error_handler(void *user_data, const std::string& reason, extern "C" { CXIndex clang_createIndex(int excludeDeclarationsFromPCH, int displayDiagnostics) { - // Disable pretty stack trace functionality, which will otherwise be a very - // poor citizen of the world and set up all sorts of signal handlers. - llvm::DisablePrettyStackTrace = true; - // We use crash recovery to make some of our APIs more reliable, implicitly // enable it. llvm::CrashRecoveryContext::Enable(); @@ -2540,9 +2597,13 @@ void clang_toggleCrashRecovery(unsigned isEnabled) { CXTranslationUnit clang_createTranslationUnit(CXIndex CIdx, const char *ast_filename) { - if (!CIdx) + if (!CIdx || !ast_filename) return 0; + LOG_FUNC_SECTION { + *Log << ast_filename; + } + CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); FileSystemOptions FileSystemOpts; @@ -2842,7 +2903,7 @@ void clang_disposeTranslationUnit(CXTranslationUnit CTUnit) { delete CTUnit->StringPool; delete static_cast<CXDiagnosticSetImpl *>(CTUnit->Diagnostics); disposeOverridenCXCursorsPool(CTUnit->OverridenCursorsPool); - delete CTUnit->FormatContext; + delete CTUnit->CommentToXML; delete CTUnit; } } @@ -2995,15 +3056,12 @@ int clang_getFileUniqueID(CXFile file, CXFileUniqueID *outID) { if (!file || !outID) return 1; -#ifdef LLVM_ON_WIN32 - return 1; // inodes not supported on windows. -#else FileEntry *FEnt = static_cast<FileEntry *>(file); - outID->data[0] = FEnt->getDevice(); - outID->data[1] = FEnt->getInode(); + const llvm::sys::fs::UniqueID &ID = FEnt->getUniqueID(); + outID->data[0] = ID.getDevice(); + outID->data[1] = ID.getFile(); outID->data[2] = FEnt->getModificationTime(); return 0; -#endif } } // end: extern "C" @@ -3289,6 +3347,10 @@ CXString clang_getCursorSpelling(CXCursor C) { return cxstring::createDup(AA->getLabel()); } + if (C.kind == CXCursor_PackedAttr) { + return cxstring::createRef("packed"); + } + return cxstring::createEmpty(); } @@ -3706,6 +3768,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("attribute(annotate)"); case CXCursor_AsmLabelAttr: return cxstring::createRef("asm label"); + case CXCursor_PackedAttr: + return cxstring::createRef("attribute(packed)"); case CXCursor_PreprocessingDirective: return cxstring::createRef("preprocessing directive"); case CXCursor_MacroDefinition: @@ -3754,6 +3818,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("CXXAccessSpecifier"); case CXCursor_ModuleImportDecl: return cxstring::createRef("ModuleImport"); + case CXCursor_OMPParallelDirective: + return cxstring::createRef("OMPParallelDirective"); } llvm_unreachable("Unhandled CXCursorKind"); @@ -4111,6 +4177,12 @@ CXSourceLocation clang_getCursorLocation(CXCursor C) { return cxloc::translateSourceLocation(getCursorContext(C), L); } + if (clang_isAttribute(C.kind)) { + SourceLocation L + = cxcursor::getCursorAttr(C)->getLocation(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + if (!clang_isDeclaration(C.kind)) return clang_getNullLocation(); @@ -4521,7 +4593,9 @@ CXCursor clang_getCursorDefinition(CXCursor C) { return clang_getNullCursor(); } - case Decl::Var: { + case Decl::Var: + case Decl::VarTemplateSpecialization: + case Decl::VarTemplatePartialSpecialization: { // Ask the variable if it has a definition. if (const VarDecl *Def = cast<VarDecl>(D)->getDefinition()) return MakeCXCursor(Def, TU); @@ -4543,6 +4617,13 @@ CXCursor clang_getCursorDefinition(CXCursor C) { return clang_getNullCursor(); } + case Decl::VarTemplate: { + if (VarDecl *Def = + cast<VarTemplateDecl>(D)->getTemplatedDecl()->getDefinition()) + return MakeCXCursor(cast<VarDecl>(Def)->getDescribedVarTemplate(), TU); + return clang_getNullCursor(); + } + case Decl::Using: return MakeCursorOverloadedDeclRef(cast<UsingDecl>(D), D->getLocation(), TU); @@ -5171,6 +5252,11 @@ AnnotateTokensWorker::Visit(CXCursor cursor, CXCursor parent) { HasContextSensitiveKeywords = true; } } + + // Don't override a property annotation with its getter/setter method. + if (cursor.kind == CXCursor_ObjCInstanceMethodDecl && + parent.kind == CXCursor_ObjCPropertyDecl) + return CXChildVisit_Continue; if (clang_isPreprocessing(cursor.kind)) { // Items in the preprocessing record are kept separate from items in @@ -5678,8 +5764,9 @@ CXLinkageKind clang_getCursorLinkage(CXCursor cursor) { const Decl *D = cxcursor::getCursorDecl(cursor); if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(D)) - switch (ND->getLinkage()) { - case NoLinkage: return CXLinkage_NoLinkage; + switch (ND->getLinkageInternal()) { + case NoLinkage: + case VisibleNoLinkage: return CXLinkage_NoLinkage; case InternalLinkage: return CXLinkage_Internal; case UniqueExternalLinkage: return CXLinkage_UniqueExternal; case ExternalLinkage: return CXLinkage_External; @@ -5743,25 +5830,33 @@ static CXLanguageKind getDeclLanguage(const Decl *D) { } extern "C" { + +static CXAvailabilityKind getCursorAvailabilityForDecl(const Decl *D) { + if (isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->isDeleted()) + return CXAvailability_Available; -enum CXAvailabilityKind clang_getCursorAvailability(CXCursor cursor) { - if (clang_isDeclaration(cursor.kind)) - if (const Decl *D = cxcursor::getCursorDecl(cursor)) { - if (isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->isDeleted()) - return CXAvailability_Available; - - switch (D->getAvailability()) { - case AR_Available: - case AR_NotYetIntroduced: - return CXAvailability_Available; + switch (D->getAvailability()) { + case AR_Available: + case AR_NotYetIntroduced: + if (const EnumConstantDecl *EnumConst = dyn_cast<EnumConstantDecl>(D)) + return getCursorAvailabilityForDecl( + cast<Decl>(EnumConst->getDeclContext())); + return CXAvailability_Available; - case AR_Deprecated: - return CXAvailability_Deprecated; + case AR_Deprecated: + return CXAvailability_Deprecated; - case AR_Unavailable: - return CXAvailability_NotAvailable; - } - } + case AR_Unavailable: + return CXAvailability_NotAvailable; + } + + llvm_unreachable("Unknown availability kind!"); +} + +enum CXAvailabilityKind clang_getCursorAvailability(CXCursor cursor) { + if (clang_isDeclaration(cursor.kind)) + if (const Decl *D = cxcursor::getCursorDecl(cursor)) + return getCursorAvailabilityForDecl(D); return CXAvailability_Available; } @@ -5785,34 +5880,20 @@ static CXVersion convertVersion(VersionTuple In) { return Out; } - -int clang_getCursorPlatformAvailability(CXCursor cursor, - int *always_deprecated, - CXString *deprecated_message, - int *always_unavailable, - CXString *unavailable_message, - CXPlatformAvailability *availability, - int availability_size) { - if (always_deprecated) - *always_deprecated = 0; - if (deprecated_message) - *deprecated_message = cxstring::createEmpty(); - if (always_unavailable) - *always_unavailable = 0; - if (unavailable_message) - *unavailable_message = cxstring::createEmpty(); - - if (!clang_isDeclaration(cursor.kind)) - return 0; - - const Decl *D = cxcursor::getCursorDecl(cursor); - if (!D) - return 0; - + +static int getCursorPlatformAvailabilityForDecl(const Decl *D, + int *always_deprecated, + CXString *deprecated_message, + int *always_unavailable, + CXString *unavailable_message, + CXPlatformAvailability *availability, + int availability_size) { + bool HadAvailAttr = false; int N = 0; for (Decl::attr_iterator A = D->attr_begin(), AEnd = D->attr_end(); A != AEnd; ++A) { if (DeprecatedAttr *Deprecated = dyn_cast<DeprecatedAttr>(*A)) { + HadAvailAttr = true; if (always_deprecated) *always_deprecated = 1; if (deprecated_message) @@ -5821,6 +5902,7 @@ int clang_getCursorPlatformAvailability(CXCursor cursor, } if (UnavailableAttr *Unavailable = dyn_cast<UnavailableAttr>(*A)) { + HadAvailAttr = true; if (always_unavailable) *always_unavailable = 1; if (unavailable_message) { @@ -5830,6 +5912,7 @@ int clang_getCursorPlatformAvailability(CXCursor cursor, } if (AvailabilityAttr *Avail = dyn_cast<AvailabilityAttr>(*A)) { + HadAvailAttr = true; if (N < availability_size) { availability[N].Platform = cxstring::createDup(Avail->getPlatform()->getName()); @@ -5842,9 +5925,51 @@ int clang_getCursorPlatformAvailability(CXCursor cursor, ++N; } } + + if (!HadAvailAttr) + if (const EnumConstantDecl *EnumConst = dyn_cast<EnumConstantDecl>(D)) + return getCursorPlatformAvailabilityForDecl( + cast<Decl>(EnumConst->getDeclContext()), + always_deprecated, + deprecated_message, + always_unavailable, + unavailable_message, + availability, + availability_size); return N; } + +int clang_getCursorPlatformAvailability(CXCursor cursor, + int *always_deprecated, + CXString *deprecated_message, + int *always_unavailable, + CXString *unavailable_message, + CXPlatformAvailability *availability, + int availability_size) { + if (always_deprecated) + *always_deprecated = 0; + if (deprecated_message) + *deprecated_message = cxstring::createEmpty(); + if (always_unavailable) + *always_unavailable = 0; + if (unavailable_message) + *unavailable_message = cxstring::createEmpty(); + + if (!clang_isDeclaration(cursor.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(cursor); + if (!D) + return 0; + + return getCursorPlatformAvailabilityForDecl(D, always_deprecated, + deprecated_message, + always_unavailable, + unavailable_message, + availability, + availability_size); +} void clang_disposeCXPlatformAvailability(CXPlatformAvailability *availability) { clang_disposeString(availability->Platform); @@ -5974,6 +6099,19 @@ unsigned clang_Cursor_getObjCDeclQualifiers(CXCursor C) { return Result; } +unsigned clang_Cursor_isObjCOptional(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = getCursorDecl(C); + if (const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D)) + return PD->getPropertyImplementation() == ObjCPropertyDecl::Optional; + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) + return MD->getImplementationControl() == ObjCMethodDecl::Optional; + + return 0; +} + unsigned clang_Cursor_isVariadic(CXCursor C) { if (!clang_isDeclaration(C.kind)) return 0; @@ -6114,6 +6252,20 @@ CXFile clang_Module_getTopLevelHeader(CXTranslationUnit TU, //===----------------------------------------------------------------------===// extern "C" { +unsigned clang_CXXMethod_isPureVirtual(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const CXXMethodDecl *Method = 0; + const Decl *D = cxcursor::getCursorDecl(C); + if (const FunctionTemplateDecl *FunTmpl = + dyn_cast_or_null<FunctionTemplateDecl>(D)) + Method = dyn_cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl()); + else + Method = dyn_cast_or_null<CXXMethodDecl>(D); + return (Method && Method->isVirtual() && Method->isPure()) ? 1 : 0; +} + unsigned clang_CXXMethod_isStatic(CXCursor C) { if (!clang_isDeclaration(C.kind)) return 0; diff --git a/tools/libclang/CIndexCodeCompletion.cpp b/tools/libclang/CIndexCodeCompletion.cpp index f79de29..865bb58 100644 --- a/tools/libclang/CIndexCodeCompletion.cpp +++ b/tools/libclang/CIndexCodeCompletion.cpp @@ -27,10 +27,12 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Sema/CodeCompleteConsumer.h" +#include "clang/Sema/Sema.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Atomic.h" #include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Program.h" #include "llvm/Support/Timer.h" @@ -271,7 +273,7 @@ struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults { /// \brief Temporary files that should be removed once we have finished /// with the code-completion results. - std::vector<llvm::sys::Path> TemporaryFiles; + std::vector<std::string> TemporaryFiles; /// \brief Temporary buffers that will be deleted once we have finished with /// the code-completion results. @@ -340,7 +342,7 @@ AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() { delete [] Results; for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) - TemporaryFiles[I].eraseFromDisk(); + llvm::sys::fs::remove(TemporaryFiles[I]); for (unsigned I = 0, N = TemporaryBuffers.size(); I != N; ++I) delete TemporaryBuffers[I]; @@ -561,15 +563,13 @@ namespace { AllocatedResults.Contexts = getContextsForContextKind(contextKind, S); AllocatedResults.Selector = ""; - if (Context.getNumSelIdents() > 0) { - for (unsigned i = 0; i < Context.getNumSelIdents(); i++) { - IdentifierInfo *selIdent = Context.getSelIdents()[i]; - if (selIdent != NULL) { - StringRef selectorString = Context.getSelIdents()[i]->getName(); - AllocatedResults.Selector += selectorString; - } - AllocatedResults.Selector += ":"; - } + ArrayRef<IdentifierInfo *> SelIdents = Context.getSelIdents(); + for (ArrayRef<IdentifierInfo *>::iterator I = SelIdents.begin(), + E = SelIdents.end(); + I != E; ++I) { + if (IdentifierInfo *selIdent = *I) + AllocatedResults.Selector += selIdent->getName(); + AllocatedResults.Selector += ":"; } QualType baseType = Context.getBaseType(); diff --git a/tools/libclang/CIndexHigh.cpp b/tools/libclang/CIndexHigh.cpp index 2a55af5..c772dbb 100644 --- a/tools/libclang/CIndexHigh.cpp +++ b/tools/libclang/CIndexHigh.cpp @@ -36,7 +36,7 @@ static void getTopOverriddenMethods(CXTranslationUnit TU, return; } - for (SmallVector<CXCursor, 8>::iterator + for (SmallVectorImpl<CXCursor>::iterator I = Overridden.begin(), E = Overridden.end(); I != E; ++I) getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods); } diff --git a/tools/libclang/CIndexUSRs.cpp b/tools/libclang/CIndexUSRs.cpp index a911ce5..2b43c5b 100644 --- a/tools/libclang/CIndexUSRs.cpp +++ b/tools/libclang/CIndexUSRs.cpp @@ -14,787 +14,13 @@ #include "CIndexer.h" #include "CXCursor.h" #include "CXString.h" -#include "clang/AST/DeclTemplate.h" -#include "clang/AST/DeclVisitor.h" -#include "clang/Frontend/ASTUnit.h" +#include "clang/Index/USRGeneration.h" #include "clang/Lex/PreprocessingRecord.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; - -//===----------------------------------------------------------------------===// -// USR generation. -//===----------------------------------------------------------------------===// - -namespace { -class USRGenerator : public ConstDeclVisitor<USRGenerator> { - OwningPtr<SmallString<128> > OwnedBuf; - SmallVectorImpl<char> &Buf; - llvm::raw_svector_ostream Out; - bool IgnoreResults; - ASTContext *Context; - bool generatedLoc; - - llvm::DenseMap<const Type *, unsigned> TypeSubstitutions; - -public: - explicit USRGenerator(ASTContext *Ctx = 0, SmallVectorImpl<char> *extBuf = 0) - : OwnedBuf(extBuf ? 0 : new SmallString<128>()), - Buf(extBuf ? *extBuf : *OwnedBuf.get()), - Out(Buf), - IgnoreResults(false), - Context(Ctx), - generatedLoc(false) - { - // Add the USR space prefix. - Out << "c:"; - } - - StringRef str() { - return Out.str(); - } - - USRGenerator* operator->() { return this; } - - template <typename T> - llvm::raw_svector_ostream &operator<<(const T &x) { - Out << x; - return Out; - } - - bool ignoreResults() const { return IgnoreResults; } - - // Visitation methods from generating USRs from AST elements. - void VisitDeclContext(const DeclContext *D); - void VisitFieldDecl(const FieldDecl *D); - void VisitFunctionDecl(const FunctionDecl *D); - void VisitNamedDecl(const NamedDecl *D); - void VisitNamespaceDecl(const NamespaceDecl *D); - void VisitNamespaceAliasDecl(const NamespaceAliasDecl *D); - void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D); - void VisitClassTemplateDecl(const ClassTemplateDecl *D); - void VisitObjCContainerDecl(const ObjCContainerDecl *CD); - void VisitObjCMethodDecl(const ObjCMethodDecl *MD); - void VisitObjCPropertyDecl(const ObjCPropertyDecl *D); - void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D); - void VisitTagDecl(const TagDecl *D); - void VisitTypedefDecl(const TypedefDecl *D); - void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D); - void VisitVarDecl(const VarDecl *D); - void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D); - void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D); - void VisitLinkageSpecDecl(const LinkageSpecDecl *D) { - IgnoreResults = true; - } - void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { - IgnoreResults = true; - } - void VisitUsingDecl(const UsingDecl *D) { - IgnoreResults = true; - } - void VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) { - IgnoreResults = true; - } - void VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) { - IgnoreResults = true; - } - - /// Generate the string component containing the location of the - /// declaration. - bool GenLoc(const Decl *D); - - /// String generation methods used both by the visitation methods - /// and from other clients that want to directly generate USRs. These - /// methods do not construct complete USRs (which incorporate the parents - /// of an AST element), but only the fragments concerning the AST element - /// itself. - - /// Generate a USR for an Objective-C class. - void GenObjCClass(StringRef cls); - /// Generate a USR for an Objective-C class category. - void GenObjCCategory(StringRef cls, StringRef cat); - /// Generate a USR fragment for an Objective-C instance variable. The - /// complete USR can be created by concatenating the USR for the - /// encompassing class with this USR fragment. - void GenObjCIvar(StringRef ivar); - /// Generate a USR fragment for an Objective-C method. - void GenObjCMethod(StringRef sel, bool isInstanceMethod); - /// Generate a USR fragment for an Objective-C property. - void GenObjCProperty(StringRef prop); - /// Generate a USR for an Objective-C protocol. - void GenObjCProtocol(StringRef prot); - - void VisitType(QualType T); - void VisitTemplateParameterList(const TemplateParameterList *Params); - void VisitTemplateName(TemplateName Name); - void VisitTemplateArgument(const TemplateArgument &Arg); - - /// Emit a Decl's name using NamedDecl::printName() and return true if - /// the decl had no name. - bool EmitDeclName(const NamedDecl *D); -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Generating USRs from ASTS. -//===----------------------------------------------------------------------===// - -bool USRGenerator::EmitDeclName(const NamedDecl *D) { - Out.flush(); - const unsigned startSize = Buf.size(); - D->printName(Out); - Out.flush(); - const unsigned endSize = Buf.size(); - return startSize == endSize; -} - -static inline bool ShouldGenerateLocation(const NamedDecl *D) { - return D->getLinkage() != ExternalLinkage; -} - -void USRGenerator::VisitDeclContext(const DeclContext *DC) { - if (const NamedDecl *D = dyn_cast<NamedDecl>(DC)) - Visit(D); -} - -void USRGenerator::VisitFieldDecl(const FieldDecl *D) { - // The USR for an ivar declared in a class extension is based on the - // ObjCInterfaceDecl, not the ObjCCategoryDecl. - if (const ObjCInterfaceDecl *ID = Context->getObjContainingInterface(D)) - Visit(ID); - else - VisitDeclContext(D->getDeclContext()); - Out << (isa<ObjCIvarDecl>(D) ? "@" : "@FI@"); - if (EmitDeclName(D)) { - // Bit fields can be anonymous. - IgnoreResults = true; - return; - } -} - -void USRGenerator::VisitFunctionDecl(const FunctionDecl *D) { - if (ShouldGenerateLocation(D) && GenLoc(D)) - return; - - VisitDeclContext(D->getDeclContext()); - if (FunctionTemplateDecl *FunTmpl = D->getDescribedFunctionTemplate()) { - Out << "@FT@"; - VisitTemplateParameterList(FunTmpl->getTemplateParameters()); - } else - Out << "@F@"; - D->printName(Out); - - ASTContext &Ctx = *Context; - if (!Ctx.getLangOpts().CPlusPlus || D->isExternC()) - return; - - if (const TemplateArgumentList * - SpecArgs = D->getTemplateSpecializationArgs()) { - Out << '<'; - for (unsigned I = 0, N = SpecArgs->size(); I != N; ++I) { - Out << '#'; - VisitTemplateArgument(SpecArgs->get(I)); - } - Out << '>'; - } - - // Mangle in type information for the arguments. - for (FunctionDecl::param_const_iterator I = D->param_begin(), - E = D->param_end(); - I != E; ++I) { - Out << '#'; - if (ParmVarDecl *PD = *I) - VisitType(PD->getType()); - } - if (D->isVariadic()) - Out << '.'; - Out << '#'; - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { - if (MD->isStatic()) - Out << 'S'; - if (unsigned quals = MD->getTypeQualifiers()) - Out << (char)('0' + quals); - } -} - -void USRGenerator::VisitNamedDecl(const NamedDecl *D) { - VisitDeclContext(D->getDeclContext()); - Out << "@"; - - if (EmitDeclName(D)) { - // The string can be empty if the declaration has no name; e.g., it is - // the ParmDecl with no name for declaration of a function pointer type, - // e.g.: void (*f)(void *); - // In this case, don't generate a USR. - IgnoreResults = true; - } -} - -void USRGenerator::VisitVarDecl(const VarDecl *D) { - // VarDecls can be declared 'extern' within a function or method body, - // but their enclosing DeclContext is the function, not the TU. We need - // to check the storage class to correctly generate the USR. - if (ShouldGenerateLocation(D) && GenLoc(D)) - return; - - VisitDeclContext(D->getDeclContext()); - - // Variables always have simple names. - StringRef s = D->getName(); - - // The string can be empty if the declaration has no name; e.g., it is - // the ParmDecl with no name for declaration of a function pointer type, e.g.: - // void (*f)(void *); - // In this case, don't generate a USR. - if (s.empty()) - IgnoreResults = true; - else - Out << '@' << s; -} - -void USRGenerator::VisitNonTypeTemplateParmDecl( - const NonTypeTemplateParmDecl *D) { - GenLoc(D); - return; -} - -void USRGenerator::VisitTemplateTemplateParmDecl( - const TemplateTemplateParmDecl *D) { - GenLoc(D); - return; -} - -void USRGenerator::VisitNamespaceDecl(const NamespaceDecl *D) { - if (D->isAnonymousNamespace()) { - Out << "@aN"; - return; - } - - VisitDeclContext(D->getDeclContext()); - if (!IgnoreResults) - Out << "@N@" << D->getName(); -} - -void USRGenerator::VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { - VisitFunctionDecl(D->getTemplatedDecl()); -} - -void USRGenerator::VisitClassTemplateDecl(const ClassTemplateDecl *D) { - VisitTagDecl(D->getTemplatedDecl()); -} - -void USRGenerator::VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) { - VisitDeclContext(D->getDeclContext()); - if (!IgnoreResults) - Out << "@NA@" << D->getName(); -} - -void USRGenerator::VisitObjCMethodDecl(const ObjCMethodDecl *D) { - const DeclContext *container = D->getDeclContext(); - if (const ObjCProtocolDecl *pd = dyn_cast<ObjCProtocolDecl>(container)) { - Visit(pd); - } - else { - // The USR for a method declared in a class extension or category is based on - // the ObjCInterfaceDecl, not the ObjCCategoryDecl. - const ObjCInterfaceDecl *ID = D->getClassInterface(); - if (!ID) { - IgnoreResults = true; - return; - } - Visit(ID); - } - // Ideally we would use 'GenObjCMethod', but this is such a hot path - // for Objective-C code that we don't want to use - // DeclarationName::getAsString(). - Out << (D->isInstanceMethod() ? "(im)" : "(cm)"); - DeclarationName N(D->getSelector()); - N.printName(Out); -} - -void USRGenerator::VisitObjCContainerDecl(const ObjCContainerDecl *D) { - switch (D->getKind()) { - default: - llvm_unreachable("Invalid ObjC container."); - case Decl::ObjCInterface: - case Decl::ObjCImplementation: - GenObjCClass(D->getName()); - break; - case Decl::ObjCCategory: { - const ObjCCategoryDecl *CD = cast<ObjCCategoryDecl>(D); - const ObjCInterfaceDecl *ID = CD->getClassInterface(); - if (!ID) { - // Handle invalid code where the @interface might not - // have been specified. - // FIXME: We should be able to generate this USR even if the - // @interface isn't available. - IgnoreResults = true; - return; - } - // Specially handle class extensions, which are anonymous categories. - // We want to mangle in the location to uniquely distinguish them. - if (CD->IsClassExtension()) { - Out << "objc(ext)" << ID->getName() << '@'; - GenLoc(CD); - } - else - GenObjCCategory(ID->getName(), CD->getName()); - - break; - } - case Decl::ObjCCategoryImpl: { - const ObjCCategoryImplDecl *CD = cast<ObjCCategoryImplDecl>(D); - const ObjCInterfaceDecl *ID = CD->getClassInterface(); - if (!ID) { - // Handle invalid code where the @interface might not - // have been specified. - // FIXME: We should be able to generate this USR even if the - // @interface isn't available. - IgnoreResults = true; - return; - } - GenObjCCategory(ID->getName(), CD->getName()); - break; - } - case Decl::ObjCProtocol: - GenObjCProtocol(cast<ObjCProtocolDecl>(D)->getName()); - break; - } -} - -void USRGenerator::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { - // The USR for a property declared in a class extension or category is based - // on the ObjCInterfaceDecl, not the ObjCCategoryDecl. - if (const ObjCInterfaceDecl *ID = Context->getObjContainingInterface(D)) - Visit(ID); - else - Visit(cast<Decl>(D->getDeclContext())); - GenObjCProperty(D->getName()); -} - -void USRGenerator::VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { - if (ObjCPropertyDecl *PD = D->getPropertyDecl()) { - VisitObjCPropertyDecl(PD); - return; - } - - IgnoreResults = true; -} - -void USRGenerator::VisitTagDecl(const TagDecl *D) { - // Add the location of the tag decl to handle resolution across - // translation units. - if (ShouldGenerateLocation(D) && GenLoc(D)) - return; - - D = D->getCanonicalDecl(); - VisitDeclContext(D->getDeclContext()); - - bool AlreadyStarted = false; - if (const CXXRecordDecl *CXXRecord = dyn_cast<CXXRecordDecl>(D)) { - if (ClassTemplateDecl *ClassTmpl = CXXRecord->getDescribedClassTemplate()) { - AlreadyStarted = true; - - switch (D->getTagKind()) { - case TTK_Interface: - case TTK_Struct: Out << "@ST"; break; - case TTK_Class: Out << "@CT"; break; - case TTK_Union: Out << "@UT"; break; - case TTK_Enum: llvm_unreachable("enum template"); - } - VisitTemplateParameterList(ClassTmpl->getTemplateParameters()); - } else if (const ClassTemplatePartialSpecializationDecl *PartialSpec - = dyn_cast<ClassTemplatePartialSpecializationDecl>(CXXRecord)) { - AlreadyStarted = true; - - switch (D->getTagKind()) { - case TTK_Interface: - case TTK_Struct: Out << "@SP"; break; - case TTK_Class: Out << "@CP"; break; - case TTK_Union: Out << "@UP"; break; - case TTK_Enum: llvm_unreachable("enum partial specialization"); - } - VisitTemplateParameterList(PartialSpec->getTemplateParameters()); - } - } - - if (!AlreadyStarted) { - switch (D->getTagKind()) { - case TTK_Interface: - case TTK_Struct: Out << "@S"; break; - case TTK_Class: Out << "@C"; break; - case TTK_Union: Out << "@U"; break; - case TTK_Enum: Out << "@E"; break; - } - } - - Out << '@'; - Out.flush(); - assert(Buf.size() > 0); - const unsigned off = Buf.size() - 1; - - if (EmitDeclName(D)) { - if (const TypedefNameDecl *TD = D->getTypedefNameForAnonDecl()) { - Buf[off] = 'A'; - Out << '@' << *TD; - } - else - Buf[off] = 'a'; - } - - // For a class template specialization, mangle the template arguments. - if (const ClassTemplateSpecializationDecl *Spec - = dyn_cast<ClassTemplateSpecializationDecl>(D)) { - const TemplateArgumentList &Args = Spec->getTemplateInstantiationArgs(); - Out << '>'; - for (unsigned I = 0, N = Args.size(); I != N; ++I) { - Out << '#'; - VisitTemplateArgument(Args.get(I)); - } - } -} - -void USRGenerator::VisitTypedefDecl(const TypedefDecl *D) { - if (ShouldGenerateLocation(D) && GenLoc(D)) - return; - const DeclContext *DC = D->getDeclContext(); - if (const NamedDecl *DCN = dyn_cast<NamedDecl>(DC)) - Visit(DCN); - Out << "@T@"; - Out << D->getName(); -} - -void USRGenerator::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) { - GenLoc(D); - return; -} - -bool USRGenerator::GenLoc(const Decl *D) { - if (generatedLoc) - return IgnoreResults; - generatedLoc = true; - - // Guard against null declarations in invalid code. - if (!D) { - IgnoreResults = true; - return true; - } - - // Use the location of canonical decl. - D = D->getCanonicalDecl(); - - const SourceManager &SM = Context->getSourceManager(); - SourceLocation L = D->getLocStart(); - if (L.isInvalid()) { - IgnoreResults = true; - return true; - } - L = SM.getExpansionLoc(L); - const std::pair<FileID, unsigned> &Decomposed = SM.getDecomposedLoc(L); - const FileEntry *FE = SM.getFileEntryForID(Decomposed.first); - if (FE) { - Out << llvm::sys::path::filename(FE->getName()); - } - else { - // This case really isn't interesting. - IgnoreResults = true; - return true; - } - // Use the offest into the FileID to represent the location. Using - // a line/column can cause us to look back at the original source file, - // which is expensive. - Out << '@' << Decomposed.second; - return IgnoreResults; -} - -void USRGenerator::VisitType(QualType T) { - // This method mangles in USR information for types. It can possibly - // just reuse the naming-mangling logic used by codegen, although the - // requirements for USRs might not be the same. - ASTContext &Ctx = *Context; - - do { - T = Ctx.getCanonicalType(T); - Qualifiers Q = T.getQualifiers(); - unsigned qVal = 0; - if (Q.hasConst()) - qVal |= 0x1; - if (Q.hasVolatile()) - qVal |= 0x2; - if (Q.hasRestrict()) - qVal |= 0x4; - if(qVal) - Out << ((char) ('0' + qVal)); - - // Mangle in ObjC GC qualifiers? - - if (const PackExpansionType *Expansion = T->getAs<PackExpansionType>()) { - Out << 'P'; - T = Expansion->getPattern(); - } - - if (const BuiltinType *BT = T->getAs<BuiltinType>()) { - unsigned char c = '\0'; - switch (BT->getKind()) { - case BuiltinType::Void: - c = 'v'; break; - case BuiltinType::Bool: - c = 'b'; break; - case BuiltinType::Char_U: - case BuiltinType::UChar: - c = 'c'; break; - case BuiltinType::Char16: - c = 'q'; break; - case BuiltinType::Char32: - c = 'w'; break; - case BuiltinType::UShort: - c = 's'; break; - case BuiltinType::UInt: - c = 'i'; break; - case BuiltinType::ULong: - c = 'l'; break; - case BuiltinType::ULongLong: - c = 'k'; break; - case BuiltinType::UInt128: - c = 'j'; break; - case BuiltinType::Char_S: - case BuiltinType::SChar: - c = 'C'; break; - case BuiltinType::WChar_S: - case BuiltinType::WChar_U: - c = 'W'; break; - case BuiltinType::Short: - c = 'S'; break; - case BuiltinType::Int: - c = 'I'; break; - case BuiltinType::Long: - c = 'L'; break; - case BuiltinType::LongLong: - c = 'K'; break; - case BuiltinType::Int128: - c = 'J'; break; - case BuiltinType::Half: - c = 'h'; break; - case BuiltinType::Float: - c = 'f'; break; - case BuiltinType::Double: - c = 'd'; break; - case BuiltinType::LongDouble: - c = 'D'; break; - case BuiltinType::NullPtr: - c = 'n'; break; -#define BUILTIN_TYPE(Id, SingletonId) -#define PLACEHOLDER_TYPE(Id, SingletonId) case BuiltinType::Id: -#include "clang/AST/BuiltinTypes.def" - case BuiltinType::Dependent: - case BuiltinType::OCLImage1d: - case BuiltinType::OCLImage1dArray: - case BuiltinType::OCLImage1dBuffer: - case BuiltinType::OCLImage2d: - case BuiltinType::OCLImage2dArray: - case BuiltinType::OCLImage3d: - case BuiltinType::OCLEvent: - case BuiltinType::OCLSampler: - IgnoreResults = true; - return; - case BuiltinType::ObjCId: - c = 'o'; break; - case BuiltinType::ObjCClass: - c = 'O'; break; - case BuiltinType::ObjCSel: - c = 'e'; break; - } - Out << c; - return; - } - - // If we have already seen this (non-built-in) type, use a substitution - // encoding. - llvm::DenseMap<const Type *, unsigned>::iterator Substitution - = TypeSubstitutions.find(T.getTypePtr()); - if (Substitution != TypeSubstitutions.end()) { - Out << 'S' << Substitution->second << '_'; - return; - } else { - // Record this as a substitution. - unsigned Number = TypeSubstitutions.size(); - TypeSubstitutions[T.getTypePtr()] = Number; - } - - if (const PointerType *PT = T->getAs<PointerType>()) { - Out << '*'; - T = PT->getPointeeType(); - continue; - } - if (const ReferenceType *RT = T->getAs<ReferenceType>()) { - Out << '&'; - T = RT->getPointeeType(); - continue; - } - if (const FunctionProtoType *FT = T->getAs<FunctionProtoType>()) { - Out << 'F'; - VisitType(FT->getResultType()); - for (FunctionProtoType::arg_type_iterator - I = FT->arg_type_begin(), E = FT->arg_type_end(); I!=E; ++I) { - VisitType(*I); - } - if (FT->isVariadic()) - Out << '.'; - return; - } - if (const BlockPointerType *BT = T->getAs<BlockPointerType>()) { - Out << 'B'; - T = BT->getPointeeType(); - continue; - } - if (const ComplexType *CT = T->getAs<ComplexType>()) { - Out << '<'; - T = CT->getElementType(); - continue; - } - if (const TagType *TT = T->getAs<TagType>()) { - Out << '$'; - VisitTagDecl(TT->getDecl()); - return; - } - if (const TemplateTypeParmType *TTP = T->getAs<TemplateTypeParmType>()) { - Out << 't' << TTP->getDepth() << '.' << TTP->getIndex(); - return; - } - if (const TemplateSpecializationType *Spec - = T->getAs<TemplateSpecializationType>()) { - Out << '>'; - VisitTemplateName(Spec->getTemplateName()); - Out << Spec->getNumArgs(); - for (unsigned I = 0, N = Spec->getNumArgs(); I != N; ++I) - VisitTemplateArgument(Spec->getArg(I)); - return; - } - - // Unhandled type. - Out << ' '; - break; - } while (true); -} - -void USRGenerator::VisitTemplateParameterList( - const TemplateParameterList *Params) { - if (!Params) - return; - Out << '>' << Params->size(); - for (TemplateParameterList::const_iterator P = Params->begin(), - PEnd = Params->end(); - P != PEnd; ++P) { - Out << '#'; - if (isa<TemplateTypeParmDecl>(*P)) { - if (cast<TemplateTypeParmDecl>(*P)->isParameterPack()) - Out<< 'p'; - Out << 'T'; - continue; - } - - if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*P)) { - if (NTTP->isParameterPack()) - Out << 'p'; - Out << 'N'; - VisitType(NTTP->getType()); - continue; - } - - TemplateTemplateParmDecl *TTP = cast<TemplateTemplateParmDecl>(*P); - if (TTP->isParameterPack()) - Out << 'p'; - Out << 't'; - VisitTemplateParameterList(TTP->getTemplateParameters()); - } -} - -void USRGenerator::VisitTemplateName(TemplateName Name) { - if (TemplateDecl *Template = Name.getAsTemplateDecl()) { - if (TemplateTemplateParmDecl *TTP - = dyn_cast<TemplateTemplateParmDecl>(Template)) { - Out << 't' << TTP->getDepth() << '.' << TTP->getIndex(); - return; - } - - Visit(Template); - return; - } - - // FIXME: Visit dependent template names. -} - -void USRGenerator::VisitTemplateArgument(const TemplateArgument &Arg) { - switch (Arg.getKind()) { - case TemplateArgument::Null: - break; - - case TemplateArgument::Declaration: - Visit(Arg.getAsDecl()); - break; - - case TemplateArgument::NullPtr: - break; - - case TemplateArgument::TemplateExpansion: - Out << 'P'; // pack expansion of... - // Fall through - case TemplateArgument::Template: - VisitTemplateName(Arg.getAsTemplateOrTemplatePattern()); - break; - - case TemplateArgument::Expression: - // FIXME: Visit expressions. - break; - - case TemplateArgument::Pack: - Out << 'p' << Arg.pack_size(); - for (TemplateArgument::pack_iterator P = Arg.pack_begin(), PEnd = Arg.pack_end(); - P != PEnd; ++P) - VisitTemplateArgument(*P); - break; - - case TemplateArgument::Type: - VisitType(Arg.getAsType()); - break; - - case TemplateArgument::Integral: - Out << 'V'; - VisitType(Arg.getIntegralType()); - Out << Arg.getAsIntegral(); - break; - } -} - -//===----------------------------------------------------------------------===// -// General purpose USR generation methods. -//===----------------------------------------------------------------------===// - -void USRGenerator::GenObjCClass(StringRef cls) { - Out << "objc(cs)" << cls; -} - -void USRGenerator::GenObjCCategory(StringRef cls, StringRef cat) { - Out << "objc(cy)" << cls << '@' << cat; -} - -void USRGenerator::GenObjCIvar(StringRef ivar) { - Out << '@' << ivar; -} - -void USRGenerator::GenObjCMethod(StringRef meth, bool isInstanceMethod) { - Out << (isInstanceMethod ? "(im)" : "(cm)") << meth; -} - -void USRGenerator::GenObjCProperty(StringRef prop) { - Out << "(py)" << prop; -} - -void USRGenerator::GenObjCProtocol(StringRef prot) { - Out << "objc(pl)" << prot; -} +using namespace clang::index; //===----------------------------------------------------------------------===// // API hooks. @@ -805,17 +31,7 @@ static inline StringRef extractUSRSuffix(StringRef s) { } bool cxcursor::getDeclCursorUSR(const Decl *D, SmallVectorImpl<char> &Buf) { - // Don't generate USRs for things with invalid locations. - if (!D || D->getLocStart().isInvalid()) - return true; - - USRGenerator UG(&D->getASTContext(), &Buf); - UG->Visit(D); - - if (UG->ignoreResults()) - return true; - - return false; + return generateUSRForDecl(D, Buf); } extern "C" { @@ -857,12 +73,10 @@ CXString clang_getCursorUSR(CXCursor C) { if (!buf) return cxstring::createEmpty(); - { - USRGenerator UG(&cxcursor::getCursorASTUnit(C)->getASTContext(), - &buf->Data); - UG << "macro@" - << cxcursor::getCursorMacroDefinition(C)->getName()->getNameStart(); - } + buf->Data += getUSRSpacePrefix(); + buf->Data += "macro@"; + buf->Data += + cxcursor::getCursorMacroDefinition(C)->getName()->getNameStart(); buf->Data.push_back('\0'); return createCXString(buf); } @@ -871,46 +85,52 @@ CXString clang_getCursorUSR(CXCursor C) { } CXString clang_constructUSR_ObjCIvar(const char *name, CXString classUSR) { - USRGenerator UG; - UG << extractUSRSuffix(clang_getCString(classUSR)); - UG->GenObjCIvar(name); - return cxstring::createDup(UG.str()); + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + OS << extractUSRSuffix(clang_getCString(classUSR)); + generateUSRForObjCIvar(name, OS); + return cxstring::createDup(OS.str()); } CXString clang_constructUSR_ObjCMethod(const char *name, unsigned isInstanceMethod, CXString classUSR) { - USRGenerator UG; - UG << extractUSRSuffix(clang_getCString(classUSR)); - UG->GenObjCMethod(name, isInstanceMethod); - return cxstring::createDup(UG.str()); + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + OS << extractUSRSuffix(clang_getCString(classUSR)); + generateUSRForObjCMethod(name, isInstanceMethod, OS); + return cxstring::createDup(OS.str()); } CXString clang_constructUSR_ObjCClass(const char *name) { - USRGenerator UG; - UG->GenObjCClass(name); - return cxstring::createDup(UG.str()); + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + generateUSRForObjCClass(name, OS); + return cxstring::createDup(OS.str()); } CXString clang_constructUSR_ObjCProtocol(const char *name) { - USRGenerator UG; - UG->GenObjCProtocol(name); - return cxstring::createDup(UG.str()); + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + generateUSRForObjCProtocol(name, OS); + return cxstring::createDup(OS.str()); } CXString clang_constructUSR_ObjCCategory(const char *class_name, const char *category_name) { - USRGenerator UG; - UG->GenObjCCategory(class_name, category_name); - return cxstring::createDup(UG.str()); + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + generateUSRForObjCCategory(class_name, category_name, OS); + return cxstring::createDup(OS.str()); } CXString clang_constructUSR_ObjCProperty(const char *property, CXString classUSR) { - USRGenerator UG; - UG << extractUSRSuffix(clang_getCString(classUSR)); - UG->GenObjCProperty(property); - return cxstring::createDup(UG.str()); + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + OS << extractUSRSuffix(clang_getCString(classUSR)); + generateUSRForObjCProperty(property, OS); + return cxstring::createDup(OS.str()); } } // end extern "C" diff --git a/tools/libclang/CIndexer.cpp b/tools/libclang/CIndexer.cpp index d89e0a4..91154cc 100644 --- a/tools/libclang/CIndexer.cpp +++ b/tools/libclang/CIndexer.cpp @@ -43,11 +43,13 @@ using namespace clang; -std::string CIndexer::getClangResourcesPath() { +const std::string &CIndexer::getClangResourcesPath() { // Did we already compute the path? if (!ResourcesPath.empty()) - return ResourcesPath.str(); - + return ResourcesPath; + + SmallString<128> LibClangPath; + // Find the location where this library lives (libclang.dylib). #ifdef LLVM_ON_WIN32 MEMORY_BASIC_INFORMATION mbi; @@ -66,85 +68,20 @@ std::string CIndexer::getClangResourcesPath() { #endif #endif - llvm::sys::Path LibClangPath(path); - LibClangPath.eraseComponent(); + LibClangPath += llvm::sys::path::parent_path(path); #else // This silly cast below avoids a C++ warning. Dl_info info; if (dladdr((void *)(uintptr_t)clang_createTranslationUnit, &info) == 0) llvm_unreachable("Call to dladdr() failed"); - - llvm::sys::Path LibClangPath(info.dli_fname); - + // We now have the CIndex directory, locate clang relative to it. - LibClangPath.eraseComponent(); + LibClangPath += llvm::sys::path::parent_path(info.dli_fname); #endif - - LibClangPath.appendComponent("clang"); - LibClangPath.appendComponent(CLANG_VERSION_STRING); - - // Cache our result. - ResourcesPath = LibClangPath; - return LibClangPath.str(); -} -static llvm::sys::Path GetTemporaryPath() { - // FIXME: This is lame; sys::Path should provide this function (in particular, - // it should know how to find the temporary files dir). - std::string Error; - const char *TmpDir = ::getenv("TMPDIR"); - if (!TmpDir) - TmpDir = ::getenv("TEMP"); - if (!TmpDir) - TmpDir = ::getenv("TMP"); - if (!TmpDir) - TmpDir = "/tmp"; - llvm::sys::Path P(TmpDir); - P.appendComponent("remap"); - if (P.makeUnique(false, &Error)) - return llvm::sys::Path(""); + llvm::sys::path::append(LibClangPath, "clang", CLANG_VERSION_STRING); - // FIXME: Grumble, makeUnique sometimes leaves the file around!? PR3837. - P.eraseFromDisk(false, 0); - - return P; -} - -bool clang::RemapFiles(unsigned num_unsaved_files, - struct CXUnsavedFile *unsaved_files, - std::vector<std::string> &RemapArgs, - std::vector<llvm::sys::Path> &TemporaryFiles) { - for (unsigned i = 0; i != num_unsaved_files; ++i) { - // Write the contents of this unsaved file into the temporary file. - llvm::sys::Path SavedFile(GetTemporaryPath()); - if (SavedFile.empty()) - return true; - - std::string ErrorInfo; - llvm::raw_fd_ostream OS(SavedFile.c_str(), ErrorInfo, - llvm::raw_fd_ostream::F_Binary); - if (!ErrorInfo.empty()) - return true; - - OS.write(unsaved_files[i].Contents, unsaved_files[i].Length); - OS.close(); - if (OS.has_error()) { - SavedFile.eraseFromDisk(); - OS.clear_error(); - return true; - } - - // Remap the file. - std::string RemapArg = unsaved_files[i].Filename; - RemapArg += ';'; - RemapArg += SavedFile.str(); - RemapArgs.push_back("-Xclang"); - RemapArgs.push_back("-remap-file"); - RemapArgs.push_back("-Xclang"); - RemapArgs.push_back(RemapArg); - TemporaryFiles.push_back(SavedFile); - } - - return false; + // Cache our result. + ResourcesPath = LibClangPath.str(); + return ResourcesPath; } - diff --git a/tools/libclang/CIndexer.h b/tools/libclang/CIndexer.h index 08162c5..95d8115 100644 --- a/tools/libclang/CIndexer.h +++ b/tools/libclang/CIndexer.h @@ -37,7 +37,7 @@ class CIndexer { bool DisplayDiagnostics; unsigned Options; // CXGlobalOptFlags. - llvm::sys::Path ResourcesPath; + std::string ResourcesPath; public: CIndexer() : OnlyLocalDecls(false), DisplayDiagnostics(false), @@ -62,20 +62,9 @@ public: } /// \brief Get the path of the clang resource files. - std::string getClangResourcesPath(); + const std::string &getClangResourcesPath(); }; - /** - * \brief Given a set of "unsaved" files, create temporary files and - * construct the clang -cc1 argument list needed to perform the remapping. - * - * \returns true if an error occurred. - */ - bool RemapFiles(unsigned num_unsaved_files, - struct CXUnsavedFile *unsaved_files, - std::vector<std::string> &RemapArgs, - std::vector<llvm::sys::Path> &TemporaryFiles); - /// \brief Return the current size to request for "safety". unsigned GetSafetyThreadStackSize(); diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index c5a975b..b7bde69 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -39,11 +39,11 @@ set(SOURCES Indexing.cpp IndexingContext.cpp IndexingContext.h - SimpleFormatContext.h ../../include/clang-c/Index.h ) set(LIBRARIES + clangIndex clangARCMigrate clangRewriteCore clangRewriteFrontend @@ -56,7 +56,6 @@ set(LIBRARIES clangLex clangTooling clangBasic - clangFormat ) set(GENERATED_HEADERS @@ -70,6 +69,8 @@ set(GENERATED_HEADERS ClangStmtNodes ) +set(EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/libclang.exports) + if( LLVM_ENABLE_PIC ) set(SHARED_LIBRARY TRUE) add_clang_library(libclang ${SOURCES}) @@ -92,11 +93,17 @@ if( LLVM_ENABLE_PIC ) if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(LIBCLANG_LINK_FLAGS - "-Wl,-compatibility_version -Wl,1 -Wl,-dead_strip") + " -Wl,-compatibility_version -Wl,1 -Wl,-dead_strip") + if (DEFINED ${LLVM_SUBMIT_VERSION}) + set(LIBCLANG_LINK_FLAGS + "${LIBCLANG_LINK_FLAGS} -Wl,-current_version -Wl,${LLVM_SUBMIT_VERSION}.${LLVM_SUBMIT_SUBVERSION}") + endif() + + set_property(TARGET libclang APPEND_STRING PROPERTY + LINK_FLAGS ${LIBCLANG_LINK_FLAGS}) set_target_properties(libclang PROPERTIES - LINK_FLAGS "${LIBCLANG_LINK_FLAGS}" - INSTALL_NAME_DIR "@executable_path/../lib") + INSTALL_NAME_DIR "@rpath") endif() @@ -105,7 +112,10 @@ else() set(LIBCLANG_STATIC_TARGET_NAME libclang) endif() -if( NOT BUILD_SHARED_LIBS AND NOT WIN32 ) +option(LIBCLANG_BUILD_STATIC + "Build libclang as a static library (in addition to a shared one)" OFF) + +if( (NOT LLVM_ENABLE_PIC OR LIBCLANG_BUILD_STATIC) AND NOT WIN32 ) add_clang_library(${LIBCLANG_STATIC_TARGET_NAME} STATIC ${SOURCES}) target_link_libraries(${LIBCLANG_STATIC_TARGET_NAME} ${LIBRARIES}) add_dependencies(${LIBCLANG_STATIC_TARGET_NAME} ${GENERATED_HEADERS} clang-headers) diff --git a/tools/libclang/CXComment.cpp b/tools/libclang/CXComment.cpp index 1c127e1..2c4f269 100644 --- a/tools/libclang/CXComment.cpp +++ b/tools/libclang/CXComment.cpp @@ -15,17 +15,11 @@ #include "CXComment.h" #include "CXCursor.h" #include "CXString.h" -#include "SimpleFormatContext.h" -#include "clang/AST/CommentCommandTraits.h" -#include "clang/AST/CommentVisitor.h" #include "clang/AST/Decl.h" -#include "clang/AST/PrettyPrinter.h" -#include "clang/Format/Format.h" -#include "clang/Lex/Lexer.h" +#include "clang/Index/CommentToXML.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/raw_ostream.h" #include <climits> using namespace clang; @@ -268,7 +262,7 @@ unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) { unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) { const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); - if (!PCC || !PCC->isParamIndexValid()) + if (!PCC || !PCC->isParamIndexValid() || PCC->isVarArgParam()) return ParamCommandComment::InvalidParamIndex; return PCC->getParamIndex(); @@ -350,499 +344,23 @@ CXString clang_VerbatimLineComment_getText(CXComment CXC) { return cxstring::createRef(VLC->getText()); } -} // end extern "C" - //===----------------------------------------------------------------------===// -// Helpers for converting comment AST to HTML. +// Converting comments to XML. //===----------------------------------------------------------------------===// -namespace { - -/// This comparison will sort parameters with valid index by index and -/// invalid (unresolved) parameters last. -class ParamCommandCommentCompareIndex { -public: - bool operator()(const ParamCommandComment *LHS, - const ParamCommandComment *RHS) const { - unsigned LHSIndex = UINT_MAX; - unsigned RHSIndex = UINT_MAX; - if (LHS->isParamIndexValid()) - LHSIndex = LHS->getParamIndex(); - if (RHS->isParamIndexValid()) - RHSIndex = RHS->getParamIndex(); - - return LHSIndex < RHSIndex; - } -}; - -/// This comparison will sort template parameters in the following order: -/// \li real template parameters (depth = 1) in index order; -/// \li all other names (depth > 1); -/// \li unresolved names. -class TParamCommandCommentComparePosition { -public: - bool operator()(const TParamCommandComment *LHS, - const TParamCommandComment *RHS) const { - // Sort unresolved names last. - if (!LHS->isPositionValid()) - return false; - if (!RHS->isPositionValid()) - return true; - - if (LHS->getDepth() > 1) - return false; - if (RHS->getDepth() > 1) - return true; - - // Sort template parameters in index order. - if (LHS->getDepth() == 1 && RHS->getDepth() == 1) - return LHS->getIndex(0) < RHS->getIndex(0); - - // Leave all other names in source order. - return true; - } -}; - -/// Separate parts of a FullComment. -struct FullCommentParts { - /// Take a full comment apart and initialize members accordingly. - FullCommentParts(const FullComment *C, - const CommandTraits &Traits); - - const BlockContentComment *Brief; - const BlockContentComment *Headerfile; - const ParagraphComment *FirstParagraph; - const BlockCommandComment *Returns; - SmallVector<const ParamCommandComment *, 8> Params; - SmallVector<const TParamCommandComment *, 4> TParams; - SmallVector<const BlockContentComment *, 8> MiscBlocks; -}; - -FullCommentParts::FullCommentParts(const FullComment *C, - const CommandTraits &Traits) : - Brief(NULL), Headerfile(NULL), FirstParagraph(NULL), Returns(NULL) { - for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); - I != E; ++I) { - const Comment *Child = *I; - if (!Child) - continue; - switch (Child->getCommentKind()) { - case Comment::NoCommentKind: - continue; - - case Comment::ParagraphCommentKind: { - const ParagraphComment *PC = cast<ParagraphComment>(Child); - if (PC->isWhitespace()) - break; - if (!FirstParagraph) - FirstParagraph = PC; - - MiscBlocks.push_back(PC); - break; - } - - case Comment::BlockCommandCommentKind: { - const BlockCommandComment *BCC = cast<BlockCommandComment>(Child); - const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID()); - if (!Brief && Info->IsBriefCommand) { - Brief = BCC; - break; - } - if (!Headerfile && Info->IsHeaderfileCommand) { - Headerfile = BCC; - break; - } - if (!Returns && Info->IsReturnsCommand) { - Returns = BCC; - break; - } - MiscBlocks.push_back(BCC); - break; - } - - case Comment::ParamCommandCommentKind: { - const ParamCommandComment *PCC = cast<ParamCommandComment>(Child); - if (!PCC->hasParamName()) - break; - - if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph()) - break; - - Params.push_back(PCC); - break; - } - - case Comment::TParamCommandCommentKind: { - const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child); - if (!TPCC->hasParamName()) - break; - - if (!TPCC->hasNonWhitespaceParagraph()) - break; - - TParams.push_back(TPCC); - break; - } - - case Comment::VerbatimBlockCommentKind: - MiscBlocks.push_back(cast<BlockCommandComment>(Child)); - break; - - case Comment::VerbatimLineCommentKind: { - const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child); - const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID()); - if (!Info->IsDeclarationCommand) - MiscBlocks.push_back(VLC); - break; - } - - case Comment::TextCommentKind: - case Comment::InlineCommandCommentKind: - case Comment::HTMLStartTagCommentKind: - case Comment::HTMLEndTagCommentKind: - case Comment::VerbatimBlockLineCommentKind: - case Comment::FullCommentKind: - llvm_unreachable("AST node of this kind can't be a child of " - "a FullComment"); - } - } - - // Sort params in order they are declared in the function prototype. - // Unresolved parameters are put at the end of the list in the same order - // they were seen in the comment. - std::stable_sort(Params.begin(), Params.end(), - ParamCommandCommentCompareIndex()); - - std::stable_sort(TParams.begin(), TParams.end(), - TParamCommandCommentComparePosition()); -} - -void PrintHTMLStartTagComment(const HTMLStartTagComment *C, - llvm::raw_svector_ostream &Result) { - Result << "<" << C->getTagName(); - - if (C->getNumAttrs() != 0) { - for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) { - Result << " "; - const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); - Result << Attr.Name; - if (!Attr.Value.empty()) - Result << "=\"" << Attr.Value << "\""; - } - } - - if (!C->isSelfClosing()) - Result << ">"; - else - Result << "/>"; -} - -class CommentASTToHTMLConverter : - public ConstCommentVisitor<CommentASTToHTMLConverter> { -public: - /// \param Str accumulator for HTML. - CommentASTToHTMLConverter(const FullComment *FC, - SmallVectorImpl<char> &Str, - const CommandTraits &Traits) : - FC(FC), Result(Str), Traits(Traits) - { } - - // Inline content. - void visitTextComment(const TextComment *C); - void visitInlineCommandComment(const InlineCommandComment *C); - void visitHTMLStartTagComment(const HTMLStartTagComment *C); - void visitHTMLEndTagComment(const HTMLEndTagComment *C); - - // Block content. - void visitParagraphComment(const ParagraphComment *C); - void visitBlockCommandComment(const BlockCommandComment *C); - void visitParamCommandComment(const ParamCommandComment *C); - void visitTParamCommandComment(const TParamCommandComment *C); - void visitVerbatimBlockComment(const VerbatimBlockComment *C); - void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); - void visitVerbatimLineComment(const VerbatimLineComment *C); - - void visitFullComment(const FullComment *C); - - // Helpers. - - /// Convert a paragraph that is not a block by itself (an argument to some - /// command). - void visitNonStandaloneParagraphComment(const ParagraphComment *C); - - void appendToResultWithHTMLEscaping(StringRef S); - -private: - const FullComment *FC; - /// Output stream for HTML. - llvm::raw_svector_ostream Result; - - const CommandTraits &Traits; -}; -} // end unnamed namespace - -void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { - appendToResultWithHTMLEscaping(C->getText()); -} - -void CommentASTToHTMLConverter::visitInlineCommandComment( - const InlineCommandComment *C) { - // Nothing to render if no arguments supplied. - if (C->getNumArgs() == 0) - return; - - // Nothing to render if argument is empty. - StringRef Arg0 = C->getArgText(0); - if (Arg0.empty()) - return; - - switch (C->getRenderKind()) { - case InlineCommandComment::RenderNormal: - for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { - appendToResultWithHTMLEscaping(C->getArgText(i)); - Result << " "; - } - return; - - case InlineCommandComment::RenderBold: - assert(C->getNumArgs() == 1); - Result << "<b>"; - appendToResultWithHTMLEscaping(Arg0); - Result << "</b>"; - return; - case InlineCommandComment::RenderMonospaced: - assert(C->getNumArgs() == 1); - Result << "<tt>"; - appendToResultWithHTMLEscaping(Arg0); - Result<< "</tt>"; - return; - case InlineCommandComment::RenderEmphasized: - assert(C->getNumArgs() == 1); - Result << "<em>"; - appendToResultWithHTMLEscaping(Arg0); - Result << "</em>"; - return; - } -} - -void CommentASTToHTMLConverter::visitHTMLStartTagComment( - const HTMLStartTagComment *C) { - PrintHTMLStartTagComment(C, Result); -} - -void CommentASTToHTMLConverter::visitHTMLEndTagComment( - const HTMLEndTagComment *C) { - Result << "</" << C->getTagName() << ">"; -} - -void CommentASTToHTMLConverter::visitParagraphComment( - const ParagraphComment *C) { - if (C->isWhitespace()) - return; - - Result << "<p>"; - for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); - I != E; ++I) { - visit(*I); - } - Result << "</p>"; -} - -void CommentASTToHTMLConverter::visitBlockCommandComment( - const BlockCommandComment *C) { - const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID()); - if (Info->IsBriefCommand) { - Result << "<p class=\"para-brief\">"; - visitNonStandaloneParagraphComment(C->getParagraph()); - Result << "</p>"; - return; - } - if (Info->IsReturnsCommand) { - Result << "<p class=\"para-returns\">" - "<span class=\"word-returns\">Returns</span> "; - visitNonStandaloneParagraphComment(C->getParagraph()); - Result << "</p>"; - return; - } - // We don't know anything about this command. Just render the paragraph. - visit(C->getParagraph()); -} - -void CommentASTToHTMLConverter::visitParamCommandComment( - const ParamCommandComment *C) { - if (C->isParamIndexValid()) { - Result << "<dt class=\"param-name-index-" - << C->getParamIndex() - << "\">"; - appendToResultWithHTMLEscaping(C->getParamName(FC)); - } else { - Result << "<dt class=\"param-name-index-invalid\">"; - appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); - } - Result << "</dt>"; - - if (C->isParamIndexValid()) { - Result << "<dd class=\"param-descr-index-" - << C->getParamIndex() - << "\">"; - } else - Result << "<dd class=\"param-descr-index-invalid\">"; - - visitNonStandaloneParagraphComment(C->getParagraph()); - Result << "</dd>"; -} - -void CommentASTToHTMLConverter::visitTParamCommandComment( - const TParamCommandComment *C) { - if (C->isPositionValid()) { - if (C->getDepth() == 1) - Result << "<dt class=\"tparam-name-index-" - << C->getIndex(0) - << "\">"; - else - Result << "<dt class=\"tparam-name-index-other\">"; - appendToResultWithHTMLEscaping(C->getParamName(FC)); - } else { - Result << "<dt class=\"tparam-name-index-invalid\">"; - appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); - } - - Result << "</dt>"; - - if (C->isPositionValid()) { - if (C->getDepth() == 1) - Result << "<dd class=\"tparam-descr-index-" - << C->getIndex(0) - << "\">"; - else - Result << "<dd class=\"tparam-descr-index-other\">"; - } else - Result << "<dd class=\"tparam-descr-index-invalid\">"; - - visitNonStandaloneParagraphComment(C->getParagraph()); - Result << "</dd>"; -} - -void CommentASTToHTMLConverter::visitVerbatimBlockComment( - const VerbatimBlockComment *C) { - unsigned NumLines = C->getNumLines(); - if (NumLines == 0) - return; - - Result << "<pre>"; - for (unsigned i = 0; i != NumLines; ++i) { - appendToResultWithHTMLEscaping(C->getText(i)); - if (i + 1 != NumLines) - Result << '\n'; - } - Result << "</pre>"; -} - -void CommentASTToHTMLConverter::visitVerbatimBlockLineComment( - const VerbatimBlockLineComment *C) { - llvm_unreachable("should not see this AST node"); -} - -void CommentASTToHTMLConverter::visitVerbatimLineComment( - const VerbatimLineComment *C) { - Result << "<pre>"; - appendToResultWithHTMLEscaping(C->getText()); - Result << "</pre>"; -} - -void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) { - FullCommentParts Parts(C, Traits); - - bool FirstParagraphIsBrief = false; - if (Parts.Headerfile) - visit(Parts.Headerfile); - if (Parts.Brief) - visit(Parts.Brief); - else if (Parts.FirstParagraph) { - Result << "<p class=\"para-brief\">"; - visitNonStandaloneParagraphComment(Parts.FirstParagraph); - Result << "</p>"; - FirstParagraphIsBrief = true; - } - - for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { - const Comment *C = Parts.MiscBlocks[i]; - if (FirstParagraphIsBrief && C == Parts.FirstParagraph) - continue; - visit(C); - } - - if (Parts.TParams.size() != 0) { - Result << "<dl>"; - for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) - visit(Parts.TParams[i]); - Result << "</dl>"; - } - - if (Parts.Params.size() != 0) { - Result << "<dl>"; - for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) - visit(Parts.Params[i]); - Result << "</dl>"; - } - - if (Parts.Returns) - visit(Parts.Returns); - - Result.flush(); -} - -void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( - const ParagraphComment *C) { - if (!C) - return; - - for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); - I != E; ++I) { - visit(*I); - } -} - -void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { - for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { - const char C = *I; - switch (C) { - case '&': - Result << "&"; - break; - case '<': - Result << "<"; - break; - case '>': - Result << ">"; - break; - case '"': - Result << """; - break; - case '\'': - Result << "'"; - break; - case '/': - Result << "/"; - break; - default: - Result << C; - break; - } - } -} - -extern "C" { - CXString clang_HTMLTagComment_getAsString(CXComment CXC) { const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); if (!HTC) return cxstring::createNull(); - SmallString<128> HTML; - CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC)); - Converter.visit(HTC); - return cxstring::createDup(HTML.str()); + CXTranslationUnit TU = CXC.TranslationUnit; + if (!TU->CommentToXML) + TU->CommentToXML = new clang::index::CommentToXMLConverter(); + + SmallString<128> Text; + TU->CommentToXML->convertHTMLTagNodeToText( + HTC, Text, cxtu::getASTUnit(TU)->getASTContext()); + return cxstring::createDup(Text.str()); } CXString clang_FullComment_getAsHTML(CXComment CXC) { @@ -850,596 +368,28 @@ CXString clang_FullComment_getAsHTML(CXComment CXC) { if (!FC) return cxstring::createNull(); + CXTranslationUnit TU = CXC.TranslationUnit; + if (!TU->CommentToXML) + TU->CommentToXML = new clang::index::CommentToXMLConverter(); + SmallString<1024> HTML; - CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC)); - Converter.visit(FC); + TU->CommentToXML + ->convertCommentToHTML(FC, HTML, cxtu::getASTUnit(TU)->getASTContext()); return cxstring::createDup(HTML.str()); } -} // end extern "C" - -namespace { -class CommentASTToXMLConverter : - public ConstCommentVisitor<CommentASTToXMLConverter> { -public: - /// \param Str accumulator for XML. - CommentASTToXMLConverter(const FullComment *FC, - SmallVectorImpl<char> &Str, - const CommandTraits &Traits, - const SourceManager &SM, - SimpleFormatContext &SFC, - unsigned FUID) : - FC(FC), Result(Str), Traits(Traits), SM(SM), - FormatRewriterContext(SFC), - FormatInMemoryUniqueId(FUID) { } - - // Inline content. - void visitTextComment(const TextComment *C); - void visitInlineCommandComment(const InlineCommandComment *C); - void visitHTMLStartTagComment(const HTMLStartTagComment *C); - void visitHTMLEndTagComment(const HTMLEndTagComment *C); - - // Block content. - void visitParagraphComment(const ParagraphComment *C); - - void appendParagraphCommentWithKind(const ParagraphComment *C, - StringRef Kind); - - void visitBlockCommandComment(const BlockCommandComment *C); - void visitParamCommandComment(const ParamCommandComment *C); - void visitTParamCommandComment(const TParamCommandComment *C); - void visitVerbatimBlockComment(const VerbatimBlockComment *C); - void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); - void visitVerbatimLineComment(const VerbatimLineComment *C); - - void visitFullComment(const FullComment *C); - - // Helpers. - void appendToResultWithXMLEscaping(StringRef S); - - void formatTextOfDeclaration(const DeclInfo *DI, - SmallString<128> &Declaration); - -private: - const FullComment *FC; - - /// Output stream for XML. - llvm::raw_svector_ostream Result; - - const CommandTraits &Traits; - const SourceManager &SM; - SimpleFormatContext &FormatRewriterContext; - unsigned FormatInMemoryUniqueId; -}; - -void getSourceTextOfDeclaration(const DeclInfo *ThisDecl, - SmallVectorImpl<char> &Str) { - ASTContext &Context = ThisDecl->CurrentDecl->getASTContext(); - const LangOptions &LangOpts = Context.getLangOpts(); - llvm::raw_svector_ostream OS(Str); - PrintingPolicy PPolicy(LangOpts); - PPolicy.PolishForDeclaration = true; - PPolicy.TerseOutput = true; - ThisDecl->CurrentDecl->print(OS, PPolicy, - /*Indentation*/0, /*PrintInstantiation*/false); -} - -void CommentASTToXMLConverter::formatTextOfDeclaration( - const DeclInfo *DI, - SmallString<128> &Declaration) { - // FIXME. formatting API expects null terminated input string. - // There might be more efficient way of doing this. - std::string StringDecl = Declaration.str(); - - // Formatter specific code. - // Form a unique in memory buffer name. - SmallString<128> filename; - filename += "xmldecl"; - filename += llvm::utostr(FormatInMemoryUniqueId); - filename += ".xd"; - FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl); - SourceLocation Start = - FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0); - unsigned Length = Declaration.size(); - - std::vector<CharSourceRange> - Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length))); - ASTContext &Context = DI->CurrentDecl->getASTContext(); - const LangOptions &LangOpts = Context.getLangOpts(); - Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID), - FormatRewriterContext.Sources, LangOpts); - tooling::Replacements Replace = - reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges); - applyAllReplacements(Replace, FormatRewriterContext.Rewrite); - Declaration = FormatRewriterContext.getRewrittenText(ID); -} - -} // end unnamed namespace - -void CommentASTToXMLConverter::visitTextComment(const TextComment *C) { - appendToResultWithXMLEscaping(C->getText()); -} - -void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) { - // Nothing to render if no arguments supplied. - if (C->getNumArgs() == 0) - return; - - // Nothing to render if argument is empty. - StringRef Arg0 = C->getArgText(0); - if (Arg0.empty()) - return; - - switch (C->getRenderKind()) { - case InlineCommandComment::RenderNormal: - for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { - appendToResultWithXMLEscaping(C->getArgText(i)); - Result << " "; - } - return; - case InlineCommandComment::RenderBold: - assert(C->getNumArgs() == 1); - Result << "<bold>"; - appendToResultWithXMLEscaping(Arg0); - Result << "</bold>"; - return; - case InlineCommandComment::RenderMonospaced: - assert(C->getNumArgs() == 1); - Result << "<monospaced>"; - appendToResultWithXMLEscaping(Arg0); - Result << "</monospaced>"; - return; - case InlineCommandComment::RenderEmphasized: - assert(C->getNumArgs() == 1); - Result << "<emphasized>"; - appendToResultWithXMLEscaping(Arg0); - Result << "</emphasized>"; - return; - } -} - -void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) { - Result << "<rawHTML><![CDATA["; - PrintHTMLStartTagComment(C, Result); - Result << "]]></rawHTML>"; -} - -void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { - Result << "<rawHTML></" << C->getTagName() << "></rawHTML>"; -} - -void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { - appendParagraphCommentWithKind(C, StringRef()); -} - -void CommentASTToXMLConverter::appendParagraphCommentWithKind( - const ParagraphComment *C, - StringRef ParagraphKind) { - if (C->isWhitespace()) - return; - - if (ParagraphKind.empty()) - Result << "<Para>"; - else - Result << "<Para kind=\"" << ParagraphKind << "\">"; - - for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); - I != E; ++I) { - visit(*I); - } - Result << "</Para>"; -} - -void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) { - StringRef ParagraphKind; - - switch (C->getCommandID()) { - case CommandTraits::KCI_attention: - case CommandTraits::KCI_author: - case CommandTraits::KCI_authors: - case CommandTraits::KCI_bug: - case CommandTraits::KCI_copyright: - case CommandTraits::KCI_date: - case CommandTraits::KCI_invariant: - case CommandTraits::KCI_note: - case CommandTraits::KCI_post: - case CommandTraits::KCI_pre: - case CommandTraits::KCI_remark: - case CommandTraits::KCI_remarks: - case CommandTraits::KCI_sa: - case CommandTraits::KCI_see: - case CommandTraits::KCI_since: - case CommandTraits::KCI_todo: - case CommandTraits::KCI_version: - case CommandTraits::KCI_warning: - ParagraphKind = C->getCommandName(Traits); - default: - break; - } - - appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); -} - -void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) { - Result << "<Parameter><Name>"; - appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC) - : C->getParamNameAsWritten()); - Result << "</Name>"; - - if (C->isParamIndexValid()) - Result << "<Index>" << C->getParamIndex() << "</Index>"; - - Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; - switch (C->getDirection()) { - case ParamCommandComment::In: - Result << "in"; - break; - case ParamCommandComment::Out: - Result << "out"; - break; - case ParamCommandComment::InOut: - Result << "in,out"; - break; - } - Result << "</Direction><Discussion>"; - visit(C->getParagraph()); - Result << "</Discussion></Parameter>"; -} - -void CommentASTToXMLConverter::visitTParamCommandComment( - const TParamCommandComment *C) { - Result << "<Parameter><Name>"; - appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) - : C->getParamNameAsWritten()); - Result << "</Name>"; - - if (C->isPositionValid() && C->getDepth() == 1) { - Result << "<Index>" << C->getIndex(0) << "</Index>"; - } - - Result << "<Discussion>"; - visit(C->getParagraph()); - Result << "</Discussion></Parameter>"; -} - -void CommentASTToXMLConverter::visitVerbatimBlockComment( - const VerbatimBlockComment *C) { - unsigned NumLines = C->getNumLines(); - if (NumLines == 0) - return; - - switch (C->getCommandID()) { - case CommandTraits::KCI_code: - Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; - break; - default: - Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; - break; - } - for (unsigned i = 0; i != NumLines; ++i) { - appendToResultWithXMLEscaping(C->getText(i)); - if (i + 1 != NumLines) - Result << '\n'; - } - Result << "</Verbatim>"; -} - -void CommentASTToXMLConverter::visitVerbatimBlockLineComment( - const VerbatimBlockLineComment *C) { - llvm_unreachable("should not see this AST node"); -} - -void CommentASTToXMLConverter::visitVerbatimLineComment( - const VerbatimLineComment *C) { - Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; - appendToResultWithXMLEscaping(C->getText()); - Result << "</Verbatim>"; -} - -void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { - FullCommentParts Parts(C, Traits); - - const DeclInfo *DI = C->getDeclInfo(); - StringRef RootEndTag; - if (DI) { - switch (DI->getKind()) { - case DeclInfo::OtherKind: - RootEndTag = "</Other>"; - Result << "<Other"; - break; - case DeclInfo::FunctionKind: - RootEndTag = "</Function>"; - Result << "<Function"; - switch (DI->TemplateKind) { - case DeclInfo::NotTemplate: - break; - case DeclInfo::Template: - Result << " templateKind=\"template\""; - break; - case DeclInfo::TemplateSpecialization: - Result << " templateKind=\"specialization\""; - break; - case DeclInfo::TemplatePartialSpecialization: - llvm_unreachable("partial specializations of functions " - "are not allowed in C++"); - } - if (DI->IsInstanceMethod) - Result << " isInstanceMethod=\"1\""; - if (DI->IsClassMethod) - Result << " isClassMethod=\"1\""; - break; - case DeclInfo::ClassKind: - RootEndTag = "</Class>"; - Result << "<Class"; - switch (DI->TemplateKind) { - case DeclInfo::NotTemplate: - break; - case DeclInfo::Template: - Result << " templateKind=\"template\""; - break; - case DeclInfo::TemplateSpecialization: - Result << " templateKind=\"specialization\""; - break; - case DeclInfo::TemplatePartialSpecialization: - Result << " templateKind=\"partialSpecialization\""; - break; - } - break; - case DeclInfo::VariableKind: - RootEndTag = "</Variable>"; - Result << "<Variable"; - break; - case DeclInfo::NamespaceKind: - RootEndTag = "</Namespace>"; - Result << "<Namespace"; - break; - case DeclInfo::TypedefKind: - RootEndTag = "</Typedef>"; - Result << "<Typedef"; - break; - case DeclInfo::EnumKind: - RootEndTag = "</Enum>"; - Result << "<Enum"; - break; - } - - { - // Print line and column number. - SourceLocation Loc = DI->CurrentDecl->getLocation(); - std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); - FileID FID = LocInfo.first; - unsigned FileOffset = LocInfo.second; - - if (!FID.isInvalid()) { - if (const FileEntry *FE = SM.getFileEntryForID(FID)) { - Result << " file=\""; - appendToResultWithXMLEscaping(FE->getName()); - Result << "\""; - } - Result << " line=\"" << SM.getLineNumber(FID, FileOffset) - << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) - << "\""; - } - } - - // Finish the root tag. - Result << ">"; - - bool FoundName = false; - if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { - if (DeclarationName DeclName = ND->getDeclName()) { - Result << "<Name>"; - std::string Name = DeclName.getAsString(); - appendToResultWithXMLEscaping(Name); - FoundName = true; - Result << "</Name>"; - } - } - if (!FoundName) - Result << "<Name><anonymous></Name>"; - - { - // Print USR. - SmallString<128> USR; - cxcursor::getDeclCursorUSR(DI->CommentDecl, USR); - if (!USR.empty()) { - Result << "<USR>"; - appendToResultWithXMLEscaping(USR); - Result << "</USR>"; - } - } - } else { - // No DeclInfo -- just emit some root tag and name tag. - RootEndTag = "</Other>"; - Result << "<Other><Name>unknown</Name>"; - } - - if (Parts.Headerfile) { - Result << "<Headerfile>"; - visit(Parts.Headerfile); - Result << "</Headerfile>"; - } - - { - // Pretty-print the declaration. - Result << "<Declaration>"; - SmallString<128> Declaration; - getSourceTextOfDeclaration(DI, Declaration); - formatTextOfDeclaration(DI, Declaration); - appendToResultWithXMLEscaping(Declaration); - - Result << "</Declaration>"; - } - - bool FirstParagraphIsBrief = false; - if (Parts.Brief) { - Result << "<Abstract>"; - visit(Parts.Brief); - Result << "</Abstract>"; - } else if (Parts.FirstParagraph) { - Result << "<Abstract>"; - visit(Parts.FirstParagraph); - Result << "</Abstract>"; - FirstParagraphIsBrief = true; - } - - if (Parts.TParams.size() != 0) { - Result << "<TemplateParameters>"; - for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) - visit(Parts.TParams[i]); - Result << "</TemplateParameters>"; - } - - if (Parts.Params.size() != 0) { - Result << "<Parameters>"; - for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) - visit(Parts.Params[i]); - Result << "</Parameters>"; - } - - if (Parts.Returns) { - Result << "<ResultDiscussion>"; - visit(Parts.Returns); - Result << "</ResultDiscussion>"; - } - - if (DI->CommentDecl->hasAttrs()) { - const AttrVec &Attrs = DI->CommentDecl->getAttrs(); - for (unsigned i = 0, e = Attrs.size(); i != e; i++) { - const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); - if (!AA) { - if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { - if (DA->getMessage().empty()) - Result << "<Deprecated/>"; - else { - Result << "<Deprecated>"; - appendToResultWithXMLEscaping(DA->getMessage()); - Result << "</Deprecated>"; - } - } - else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { - if (UA->getMessage().empty()) - Result << "<Unavailable/>"; - else { - Result << "<Unavailable>"; - appendToResultWithXMLEscaping(UA->getMessage()); - Result << "</Unavailable>"; - } - } - continue; - } - - // 'availability' attribute. - Result << "<Availability"; - StringRef Distribution; - if (AA->getPlatform()) { - Distribution = AvailabilityAttr::getPrettyPlatformName( - AA->getPlatform()->getName()); - if (Distribution.empty()) - Distribution = AA->getPlatform()->getName(); - } - Result << " distribution=\"" << Distribution << "\">"; - VersionTuple IntroducedInVersion = AA->getIntroduced(); - if (!IntroducedInVersion.empty()) { - Result << "<IntroducedInVersion>" - << IntroducedInVersion.getAsString() - << "</IntroducedInVersion>"; - } - VersionTuple DeprecatedInVersion = AA->getDeprecated(); - if (!DeprecatedInVersion.empty()) { - Result << "<DeprecatedInVersion>" - << DeprecatedInVersion.getAsString() - << "</DeprecatedInVersion>"; - } - VersionTuple RemovedAfterVersion = AA->getObsoleted(); - if (!RemovedAfterVersion.empty()) { - Result << "<RemovedAfterVersion>" - << RemovedAfterVersion.getAsString() - << "</RemovedAfterVersion>"; - } - StringRef DeprecationSummary = AA->getMessage(); - if (!DeprecationSummary.empty()) { - Result << "<DeprecationSummary>"; - appendToResultWithXMLEscaping(DeprecationSummary); - Result << "</DeprecationSummary>"; - } - if (AA->getUnavailable()) - Result << "<Unavailable/>"; - Result << "</Availability>"; - } - } - - { - bool StartTagEmitted = false; - for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { - const Comment *C = Parts.MiscBlocks[i]; - if (FirstParagraphIsBrief && C == Parts.FirstParagraph) - continue; - if (!StartTagEmitted) { - Result << "<Discussion>"; - StartTagEmitted = true; - } - visit(C); - } - if (StartTagEmitted) - Result << "</Discussion>"; - } - - Result << RootEndTag; - - Result.flush(); -} - -void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { - for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { - const char C = *I; - switch (C) { - case '&': - Result << "&"; - break; - case '<': - Result << "<"; - break; - case '>': - Result << ">"; - break; - case '"': - Result << """; - break; - case '\'': - Result << "'"; - break; - default: - Result << C; - break; - } - } -} - -extern "C" { - CXString clang_FullComment_getAsXML(CXComment CXC) { const FullComment *FC = getASTNodeAs<FullComment>(CXC); if (!FC) return cxstring::createNull(); - ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext(); + CXTranslationUnit TU = CXC.TranslationUnit; - SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); - - if (!TU->FormatContext) { - TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); - } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) { - // Delete after some number of iterators, so the buffers don't grow - // too large. - delete TU->FormatContext; - TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); - } + if (!TU->CommentToXML) + TU->CommentToXML = new clang::index::CommentToXMLConverter(); SmallString<1024> XML; - CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM, - *TU->FormatContext, - TU->FormatInMemoryUniqueId++); - Converter.visit(FC); + TU->CommentToXML + ->convertCommentToXML(FC, XML, cxtu::getASTUnit(TU)->getASTContext()); return cxstring::createDup(XML.str()); } diff --git a/tools/libclang/CXComment.h b/tools/libclang/CXComment.h index 0780a65..1e2561d 100644 --- a/tools/libclang/CXComment.h +++ b/tools/libclang/CXComment.h @@ -27,20 +27,20 @@ namespace comments { namespace cxcomment { -inline CXComment createCXComment(const comments::Comment *C, - CXTranslationUnit TU) { +static inline CXComment createCXComment(const comments::Comment *C, + CXTranslationUnit TU) { CXComment Result; Result.ASTNode = C; Result.TranslationUnit = TU; return Result; } -inline const comments::Comment *getASTNode(CXComment CXC) { +static inline const comments::Comment *getASTNode(CXComment CXC) { return static_cast<const comments::Comment *>(CXC.ASTNode); } template<typename T> -inline const T *getASTNodeAs(CXComment CXC) { +static inline const T *getASTNodeAs(CXComment CXC) { const comments::Comment *C = getASTNode(CXC); if (!C) return NULL; @@ -48,11 +48,11 @@ inline const T *getASTNodeAs(CXComment CXC) { return dyn_cast<T>(C); } -inline ASTContext &getASTContext(CXComment CXC) { +static inline ASTContext &getASTContext(CXComment CXC) { return cxtu::getASTUnit(CXC.TranslationUnit)->getASTContext(); } -inline comments::CommandTraits &getCommandTraits(CXComment CXC) { +static inline comments::CommandTraits &getCommandTraits(CXComment CXC) { return getASTContext(CXC).getCommentCommandTraits(); } diff --git a/tools/libclang/CXCompilationDatabase.cpp b/tools/libclang/CXCompilationDatabase.cpp index e35ac27..433caec 100644 --- a/tools/libclang/CXCompilationDatabase.cpp +++ b/tools/libclang/CXCompilationDatabase.cpp @@ -1,6 +1,7 @@ #include "clang-c/CXCompilationDatabase.h" #include "CXString.h" #include "clang/Tooling/CompilationDatabase.h" +#include <cstdio> using namespace clang; using namespace clang::tooling; @@ -135,5 +136,41 @@ clang_CompileCommand_getArg(CXCompileCommand CCmd, unsigned Arg) return cxstring::createRef(Cmd->CommandLine[Arg].c_str()); } +unsigned +clang_CompileCommand_getNumMappedSources(CXCompileCommand CCmd) +{ + if (!CCmd) + return 0; + + return static_cast<CompileCommand *>(CCmd)->MappedSources.size(); +} + +CXString +clang_CompileCommand_getMappedSourcePath(CXCompileCommand CCmd, unsigned I) +{ + if (!CCmd) + return cxstring::createNull(); + + CompileCommand *Cmd = static_cast<CompileCommand *>(CCmd); + + if (I >= Cmd->MappedSources.size()) + return cxstring::createNull(); + + return cxstring::createRef(Cmd->MappedSources[I].first.c_str()); +} + +CXString +clang_CompileCommand_getMappedSourceContent(CXCompileCommand CCmd, unsigned I) +{ + if (!CCmd) + return cxstring::createNull(); + + CompileCommand *Cmd = static_cast<CompileCommand *>(CCmd); + + if (I >= Cmd->MappedSources.size()) + return cxstring::createNull(); + + return cxstring::createRef(Cmd->MappedSources[I].second.c_str()); +} } // end: extern "C" diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index 2cdb71b..c75c061 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -18,6 +18,7 @@ #include "CXString.h" #include "CXType.h" #include "clang-c/Index.h" +#include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -48,6 +49,7 @@ static CXCursorKind GetCursorKind(const Attr *A) { case attr::Override: return CXCursor_CXXOverrideAttr; case attr::Annotate: return CXCursor_AnnotateAttr; case attr::AsmLabel: return CXCursor_AsmLabelAttr; + case attr::Packed: return CXCursor_PackedAttr; } return CXCursor_UnexposedAttr; @@ -75,7 +77,7 @@ CXCursor cxcursor::MakeCXCursor(const Decl *D, CXTranslationUnit TU, RegionOfInterest.getBegin() == RegionOfInterest.getEnd()) { SmallVector<SourceLocation, 16> SelLocs; cast<ObjCMethodDecl>(D)->getSelectorLocs(SelLocs); - SmallVector<SourceLocation, 16>::iterator + SmallVectorImpl<SourceLocation>::iterator I=std::find(SelLocs.begin(), SelLocs.end(),RegionOfInterest.getBegin()); if (I != SelLocs.end()) SelectorIdIndex = I - SelLocs.begin(); @@ -216,6 +218,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::CXXBindTemporaryExprClass: case Stmt::CXXDefaultArgExprClass: case Stmt::CXXDefaultInitExprClass: + case Stmt::CXXStdInitializerListExprClass: case Stmt::CXXScalarValueInitExprClass: case Stmt::CXXUuidofExprClass: case Stmt::ChooseExprClass: @@ -231,6 +234,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::ParenListExprClass: case Stmt::PredefinedExprClass: case Stmt::ShuffleVectorExprClass: + case Stmt::ConvertVectorExprClass: case Stmt::UnaryExprOrTypeTraitExprClass: case Stmt::UnaryTypeTraitExprClass: case Stmt::VAArgExprClass: @@ -492,7 +496,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, RegionOfInterest.getBegin() == RegionOfInterest.getEnd()) { SmallVector<SourceLocation, 16> SelLocs; cast<ObjCMessageExpr>(S)->getSelectorLocs(SelLocs); - SmallVector<SourceLocation, 16>::iterator + SmallVectorImpl<SourceLocation>::iterator I=std::find(SelLocs.begin(), SelLocs.end(),RegionOfInterest.getBegin()); if (I != SelLocs.end()) SelectorIdIndex = I - SelLocs.begin(); @@ -504,6 +508,10 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::MSDependentExistsStmtClass: K = CXCursor_UnexposedStmt; break; + case Stmt::OMPParallelDirectiveClass: + K = CXCursor_OMPParallelDirective; + break; + } CXCursor C = { K, 0, { Parent, S, TU } }; @@ -836,7 +844,7 @@ void cxcursor::getOverriddenCursors(CXCursor cursor, SmallVector<const NamedDecl *, 8> OverDecls; D->getASTContext().getOverriddenMethods(D, OverDecls); - for (SmallVector<const NamedDecl *, 8>::iterator + for (SmallVectorImpl<const NamedDecl *>::iterator I = OverDecls.begin(), E = OverDecls.end(); I != E; ++I) { overridden.push_back(MakeCXCursor(*I, TU)); } diff --git a/tools/libclang/CXSourceLocation.cpp b/tools/libclang/CXSourceLocation.cpp index b7c7622..7371177 100644 --- a/tools/libclang/CXSourceLocation.cpp +++ b/tools/libclang/CXSourceLocation.cpp @@ -124,6 +124,8 @@ CXSourceLocation clang_getLocation(CXTranslationUnit TU, unsigned column) { if (!TU || !file) return clang_getNullLocation(); + if (line == 0 || column == 0) + return clang_getNullLocation(); LogRef Log = Logger::make(LLVM_FUNCTION_NAME); ASTUnit *CXXUnit = cxtu::getASTUnit(TU); @@ -209,6 +211,17 @@ int clang_Location_isInSystemHeader(CXSourceLocation location) { return SM.isInSystemHeader(Loc); } +int clang_Location_isFromMainFile(CXSourceLocation location) { + const SourceLocation Loc = + SourceLocation::getFromRawEncoding(location.int_data); + if (Loc.isInvalid()) + return 0; + + const SourceManager &SM = + *static_cast<const SourceManager*>(location.ptr_data[0]); + return SM.isWrittenInMainFile(Loc); +} + void clang_getExpansionLocation(CXSourceLocation location, CXFile *file, unsigned *line, @@ -255,7 +268,7 @@ void clang_getPresumedLocation(CXSourceLocation location, CXString *filename, unsigned *line, unsigned *column) { - + if (!isASTUnitSourceLocation(location)) { // Other SourceLocation implementations do not support presumed locations // at this time. @@ -265,20 +278,22 @@ void clang_getPresumedLocation(CXSourceLocation location, SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); - if (!location.ptr_data[0] || Loc.isInvalid()) + if (!location.ptr_data[0] || Loc.isInvalid()) { createNullLocation(filename, line, column); - else { - const SourceManager &SM = - *static_cast<const SourceManager*>(location.ptr_data[0]); - PresumedLoc PreLoc = SM.getPresumedLoc(Loc); - - if (filename) - *filename = cxstring::createRef(PreLoc.getFilename()); - if (line) - *line = PreLoc.getLine(); - if (column) - *column = PreLoc.getColumn(); + return; } + + const SourceManager &SM = + *static_cast<const SourceManager *>(location.ptr_data[0]); + PresumedLoc PreLoc = SM.getPresumedLoc(Loc); + if (PreLoc.isInvalid()) { + createNullLocation(filename, line, column); + return; + } + + if (filename) *filename = cxstring::createRef(PreLoc.getFilename()); + if (line) *line = PreLoc.getLine(); + if (column) *column = PreLoc.getColumn(); } void clang_getInstantiationLocation(CXSourceLocation location, diff --git a/tools/libclang/CXTranslationUnit.h b/tools/libclang/CXTranslationUnit.h index bdc171c..c0014c0 100644 --- a/tools/libclang/CXTranslationUnit.h +++ b/tools/libclang/CXTranslationUnit.h @@ -20,7 +20,9 @@ namespace clang { class ASTUnit; class CIndexer; - class SimpleFormatContext; +namespace index { +class CommentToXMLConverter; +} // namespace index } // namespace clang struct CXTranslationUnitImpl { @@ -29,8 +31,7 @@ struct CXTranslationUnitImpl { clang::cxstring::CXStringPool *StringPool; void *Diagnostics; void *OverridenCursorsPool; - clang::SimpleFormatContext *FormatContext; - unsigned FormatInMemoryUniqueId; + clang::index::CommentToXMLConverter *CommentToXML; }; namespace clang { diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index 1482415..1e2cb18 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -85,7 +85,11 @@ static CXTypeKind GetTypeKind(QualType T) { TKCASE(FunctionNoProto); TKCASE(FunctionProto); TKCASE(ConstantArray); + TKCASE(IncompleteArray); + TKCASE(VariableArray); + TKCASE(DependentSizedArray); TKCASE(Vector); + TKCASE(MemberPointer); default: return CXType_Unexposed; } @@ -107,6 +111,11 @@ CXType cxtype::MakeCXType(QualType T, CXTranslationUnit TU) { else if (Ctx.isObjCSelType(UnqualT)) TK = CXType_ObjCSel; } + + /* Handle decayed types as the original type */ + if (const DecayedType *DT = T->getAs<DecayedType>()) { + return MakeCXType(DT->getOriginalType(), TU); + } } if (TK == CXType_Invalid) TK = GetTypeKind(T); @@ -357,6 +366,9 @@ CXType clang_getPointeeType(CXType CT) { case Type::ObjCObjectPointer: T = cast<ObjCObjectPointerType>(TP)->getPointeeType(); break; + case Type::MemberPointer: + T = cast<MemberPointerType>(TP)->getPointeeType(); + break; default: T = QualType(); break; @@ -466,7 +478,11 @@ CXString clang_getTypeKindSpelling(enum CXTypeKind K) { TKIND(FunctionNoProto); TKIND(FunctionProto); TKIND(ConstantArray); + TKIND(IncompleteArray); + TKIND(VariableArray); + TKIND(DependentSizedArray); TKIND(Vector); + TKIND(MemberPointer); } #undef TKIND return cxstring::createRef(s); @@ -498,12 +514,13 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { if (const FunctionType *FD = T->getAs<FunctionType>()) { #define TCALLINGCONV(X) case CC_##X: return CXCallingConv_##X switch (FD->getCallConv()) { - TCALLINGCONV(Default); TCALLINGCONV(C); TCALLINGCONV(X86StdCall); TCALLINGCONV(X86FastCall); TCALLINGCONV(X86ThisCall); TCALLINGCONV(X86Pascal); + TCALLINGCONV(X86_64Win64); + TCALLINGCONV(X86_64SysV); TCALLINGCONV(AAPCS); TCALLINGCONV(AAPCS_VFP); TCALLINGCONV(PnaclCall); @@ -590,6 +607,15 @@ CXType clang_getElementType(CXType CT) { case Type::ConstantArray: ET = cast<ConstantArrayType> (TP)->getElementType(); break; + case Type::IncompleteArray: + ET = cast<IncompleteArrayType> (TP)->getElementType(); + break; + case Type::VariableArray: + ET = cast<VariableArrayType> (TP)->getElementType(); + break; + case Type::DependentSizedArray: + ET = cast<DependentSizedArrayType> (TP)->getElementType(); + break; case Type::Vector: ET = cast<VectorType> (TP)->getElementType(); break; @@ -633,6 +659,15 @@ CXType clang_getArrayElementType(CXType CT) { case Type::ConstantArray: ET = cast<ConstantArrayType> (TP)->getElementType(); break; + case Type::IncompleteArray: + ET = cast<IncompleteArrayType> (TP)->getElementType(); + break; + case Type::VariableArray: + ET = cast<VariableArrayType> (TP)->getElementType(); + break; + case Type::DependentSizedArray: + ET = cast<DependentSizedArrayType> (TP)->getElementType(); + break; default: break; } @@ -677,6 +712,17 @@ long long clang_Type_getAlignOf(CXType T) { return Ctx.getTypeAlignInChars(QT).getQuantity(); } +CXType clang_Type_getClassType(CXType CT) { + QualType ET = QualType(); + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (TP && TP->getTypeClass() == Type::MemberPointer) { + ET = QualType(cast<MemberPointerType> (TP)->getClass(), 0); + } + return MakeCXType(ET, GetTU(CT)); +} + long long clang_Type_getSizeOf(CXType T) { if (T.kind == CXType_Invalid) return CXTypeLayoutError_Invalid; @@ -733,11 +779,13 @@ long long clang_Type_getOffsetOf(CXType PT, const char *S) { return CXTypeLayoutError_Invalid; const RecordDecl *RD = dyn_cast_or_null<RecordDecl>(cxcursor::getCursorDecl(PC)); - if (!RD) + if (!RD || RD->isInvalidDecl()) return CXTypeLayoutError_Invalid; RD = RD->getDefinition(); if (!RD) return CXTypeLayoutError_Incomplete; + if (RD->isInvalidDecl()) + return CXTypeLayoutError_Invalid; QualType RT = GetQualType(PT); if (RT->isIncompleteType()) return CXTypeLayoutError_Incomplete; @@ -768,6 +816,24 @@ long long clang_Type_getOffsetOf(CXType PT, const char *S) { return CXTypeLayoutError_InvalidFieldName; } +enum CXRefQualifierKind clang_Type_getCXXRefQualifier(CXType T) { + QualType QT = GetQualType(T); + if (QT.isNull()) + return CXRefQualifier_None; + const FunctionProtoType *FD = QT->getAs<FunctionProtoType>(); + if (!FD) + return CXRefQualifier_None; + switch (FD->getRefQualifier()) { + case RQ_None: + return CXRefQualifier_None; + case RQ_LValue: + return CXRefQualifier_LValue; + case RQ_RValue: + return CXRefQualifier_RValue; + } + return CXRefQualifier_None; +} + unsigned clang_Cursor_isBitField(CXCursor C) { if (!clang_isDeclaration(C.kind)) return 0; diff --git a/tools/libclang/IndexBody.cpp b/tools/libclang/IndexBody.cpp index 02ab885..e08a346 100644 --- a/tools/libclang/IndexBody.cpp +++ b/tools/libclang/IndexBody.cpp @@ -153,9 +153,11 @@ public: if (C.capturesThis()) return true; - if (IndexCtx.shouldIndexFunctionLocalSymbols()) + if (C.capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols()) IndexCtx.handleReference(C.getCapturedVar(), C.getLocation(), Parent, ParentDC); + + // FIXME: Lambda init-captures. return true; } diff --git a/tools/libclang/IndexDecl.cpp b/tools/libclang/IndexDecl.cpp index 756001c..89feb96 100644 --- a/tools/libclang/IndexDecl.cpp +++ b/tools/libclang/IndexDecl.cpp @@ -22,6 +22,15 @@ public: explicit IndexingDeclVisitor(IndexingContext &indexCtx) : IndexCtx(indexCtx) { } + /// \brief Returns true if the given method has been defined explicitly by the + /// user. + static bool hasUserDefined(const ObjCMethodDecl *D, + const ObjCImplDecl *Container) { + const ObjCMethodDecl *MD = Container->getMethod(D->getSelector(), + D->isInstanceMethod()); + return MD && !MD->isImplicit() && MD->isThisDeclarationADefinition(); + } + void handleDeclarator(const DeclaratorDecl *D, const NamedDecl *Parent = 0) { if (!Parent) Parent = D; @@ -234,12 +243,14 @@ public: } if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) { - if (MD->isPropertyAccessor()) + if (MD->isPropertyAccessor() && + !hasUserDefined(MD, cast<ObjCImplDecl>(D->getDeclContext()))) IndexCtx.handleSynthesizedObjCMethod(MD, D->getLocation(), D->getLexicalDeclContext()); } if (ObjCMethodDecl *MD = PD->getSetterMethodDecl()) { - if (MD->isPropertyAccessor()) + if (MD->isPropertyAccessor() && + !hasUserDefined(MD, cast<ObjCImplDecl>(D->getDeclContext()))) IndexCtx.handleSynthesizedObjCMethod(MD, D->getLocation(), D->getLexicalDeclContext()); } diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp index 15786ac..99fcdb6 100644 --- a/tools/libclang/Indexing.cpp +++ b/tools/libclang/Indexing.cpp @@ -31,6 +31,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Mutex.h" #include "llvm/Support/MutexGuard.h" +#include <cstdio> using namespace clang; using namespace cxtu; @@ -88,25 +89,23 @@ public: /// #3 is identified as the location of "#ifdef CAKE" /// class PPRegion { - ino_t ino; + llvm::sys::fs::UniqueID UniqueID; time_t ModTime; - dev_t dev; unsigned Offset; public: - PPRegion() : ino(), ModTime(), dev(), Offset() {} - PPRegion(dev_t dev, ino_t ino, unsigned offset, time_t modTime) - : ino(ino), ModTime(modTime), dev(dev), Offset(offset) {} + PPRegion() : UniqueID(0, 0), ModTime(), Offset() {} + PPRegion(llvm::sys::fs::UniqueID UniqueID, unsigned offset, time_t modTime) + : UniqueID(UniqueID), ModTime(modTime), Offset(offset) {} - ino_t getIno() const { return ino; } - dev_t getDev() const { return dev; } + const llvm::sys::fs::UniqueID &getUniqueID() const { return UniqueID; } unsigned getOffset() const { return Offset; } time_t getModTime() const { return ModTime; } bool isInvalid() const { return *this == PPRegion(); } friend bool operator==(const PPRegion &lhs, const PPRegion &rhs) { - return lhs.dev == rhs.dev && lhs.ino == rhs.ino && - lhs.Offset == rhs.Offset && lhs.ModTime == rhs.ModTime; + return lhs.UniqueID == rhs.UniqueID && lhs.Offset == rhs.Offset && + lhs.ModTime == rhs.ModTime; } }; @@ -122,16 +121,17 @@ namespace llvm { template <> struct DenseMapInfo<PPRegion> { static inline PPRegion getEmptyKey() { - return PPRegion(0, 0, unsigned(-1), 0); + return PPRegion(llvm::sys::fs::UniqueID(0, 0), unsigned(-1), 0); } static inline PPRegion getTombstoneKey() { - return PPRegion(0, 0, unsigned(-2), 0); + return PPRegion(llvm::sys::fs::UniqueID(0, 0), unsigned(-2), 0); } static unsigned getHashValue(const PPRegion &S) { llvm::FoldingSetNodeID ID; - ID.AddInteger(S.getIno()); - ID.AddInteger(S.getDev()); + const llvm::sys::fs::UniqueID &UniqueID = S.getUniqueID(); + ID.AddInteger(UniqueID.getFile()); + ID.AddInteger(UniqueID.getDevice()); ID.AddInteger(S.getOffset()); ID.AddInteger(S.getModTime()); return ID.ComputeHash(); @@ -208,9 +208,10 @@ private: PPRegion getRegion(SourceLocation Loc, FileID FID, const FileEntry *FE) { SourceLocation RegionLoc = PPRec.findConditionalDirectiveRegionLoc(Loc); if (RegionLoc.isInvalid()) { - if (isParsedOnceInclude(FE)) - return PPRegion(FE->getDevice(), FE->getInode(), 0, - FE->getModificationTime()); + if (isParsedOnceInclude(FE)) { + const llvm::sys::fs::UniqueID &ID = FE->getUniqueID(); + return PPRegion(ID, 0, FE->getModificationTime()); + } return PPRegion(); } @@ -221,14 +222,15 @@ private: llvm::tie(RegionFID, RegionOffset) = SM.getDecomposedLoc(RegionLoc); if (RegionFID != FID) { - if (isParsedOnceInclude(FE)) - return PPRegion(FE->getDevice(), FE->getInode(), 0, - FE->getModificationTime()); + if (isParsedOnceInclude(FE)) { + const llvm::sys::fs::UniqueID &ID = FE->getUniqueID(); + return PPRegion(ID, 0, FE->getModificationTime()); + } return PPRegion(); } - return PPRegion(FE->getDevice(), FE->getInode(), RegionOffset, - FE->getModificationTime()); + const llvm::sys::fs::UniqueID &ID = FE->getUniqueID(); + return PPRegion(ID, RegionOffset, FE->getModificationTime()); } bool isParsedOnceInclude(const FileEntry *FE) { diff --git a/tools/libclang/IndexingContext.cpp b/tools/libclang/IndexingContext.cpp index 14b430c..41ed6ea 100644 --- a/tools/libclang/IndexingContext.cpp +++ b/tools/libclang/IndexingContext.cpp @@ -10,6 +10,7 @@ #include "IndexingContext.h" #include "CIndexDiagnostic.h" #include "CXTranslationUnit.h" +#include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/Frontend/ASTUnit.h" @@ -93,17 +94,19 @@ AttrListInfo::AttrListInfo(const Decl *D, IndexingContext &IdxCtx) const IBOutletCollectionAttr * IBAttr = cast<IBOutletCollectionAttr>(IBInfo.A); + SourceLocation InterfaceLocStart = + IBAttr->getInterfaceLoc()->getTypeLoc().getLocStart(); IBInfo.IBCollInfo.attrInfo = &IBInfo; - IBInfo.IBCollInfo.classLoc = IdxCtx.getIndexLoc(IBAttr->getInterfaceLoc()); + IBInfo.IBCollInfo.classLoc = IdxCtx.getIndexLoc(InterfaceLocStart); IBInfo.IBCollInfo.objcClass = 0; IBInfo.IBCollInfo.classCursor = clang_getNullCursor(); QualType Ty = IBAttr->getInterface(); - if (const ObjCInterfaceType *InterTy = Ty->getAs<ObjCInterfaceType>()) { - if (const ObjCInterfaceDecl *InterD = InterTy->getInterface()) { + if (const ObjCObjectType *ObjectTy = Ty->getAs<ObjCObjectType>()) { + if (const ObjCInterfaceDecl *InterD = ObjectTy->getInterface()) { IdxCtx.getEntityInfo(InterD, IBInfo.ClassInfo, SA); IBInfo.IBCollInfo.objcClass = &IBInfo.ClassInfo; - IBInfo.IBCollInfo.classCursor = MakeCursorObjCClassRef(InterD, - IBAttr->getInterfaceLoc(), IdxCtx.CXTU); + IBInfo.IBCollInfo.classCursor = + MakeCursorObjCClassRef(InterD, InterfaceLocStart, IdxCtx.CXTU); } } } @@ -210,11 +213,13 @@ bool IndexingContext::isFunctionLocalDecl(const Decl *D) { return false; if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) { - switch (ND->getLinkage()) { + switch (ND->getFormalLinkage()) { case NoLinkage: + case VisibleNoLinkage: case InternalLinkage: return true; case UniqueExternalLinkage: + llvm_unreachable("Not a sema linkage"); case ExternalLinkage: return false; } @@ -378,14 +383,14 @@ bool IndexingContext::handleFunction(const FunctionDecl *D) { isContainer = false; } - DeclInfo DInfo(!D->isFirstDeclaration(), isDef, isContainer); + DeclInfo DInfo(!D->isFirstDecl(), isDef, isContainer); if (isSkipped) DInfo.flags |= CXIdxDeclFlag_Skipped; return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool IndexingContext::handleVar(const VarDecl *D) { - DeclInfo DInfo(!D->isFirstDeclaration(), D->isThisDeclarationADefinition(), + DeclInfo DInfo(!D->isFirstDecl(), D->isThisDeclarationADefinition(), /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } @@ -412,13 +417,13 @@ bool IndexingContext::handleTagDecl(const TagDecl *D) { if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(D)) return handleCXXRecordDecl(CXXRD, D); - DeclInfo DInfo(!D->isFirstDeclaration(), D->isThisDeclarationADefinition(), + DeclInfo DInfo(!D->isFirstDecl(), D->isThisDeclarationADefinition(), D->isThisDeclarationADefinition()); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool IndexingContext::handleTypedefName(const TypedefNameDecl *D) { - DeclInfo DInfo(!D->isFirstDeclaration(), /*isDefinition=*/true, + DeclInfo DInfo(!D->isFirstDecl(), /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } diff --git a/tools/libclang/IndexingContext.h b/tools/libclang/IndexingContext.h index 62873be..3437d55 100644 --- a/tools/libclang/IndexingContext.h +++ b/tools/libclang/IndexingContext.h @@ -270,14 +270,6 @@ public: } }; -struct RefFileOccurence { - const FileEntry *File; - const Decl *Dcl; - - RefFileOccurence(const FileEntry *File, const Decl *Dcl) - : File(File), Dcl(Dcl) { } -}; - class IndexingContext { ASTContext *Ctx; CXClientData ClientData; @@ -294,6 +286,7 @@ class IndexingContext { ContainerMapTy ContainerMap; EntityMapTy EntityMap; + typedef std::pair<const FileEntry *, const Decl *> RefFileOccurence; llvm::DenseSet<RefFileOccurence> RefFileOccurences; std::deque<DeclGroupRef> TUDeclsInObjCContainer; @@ -524,29 +517,3 @@ inline T *ScratchAlloc::allocate() { } }} // end clang::cxindex - -namespace llvm { - /// Define DenseMapInfo so that FileID's can be used as keys in DenseMap and - /// DenseSets. - template <> - struct DenseMapInfo<clang::cxindex::RefFileOccurence> { - static inline clang::cxindex::RefFileOccurence getEmptyKey() { - return clang::cxindex::RefFileOccurence(0, 0); - } - - static inline clang::cxindex::RefFileOccurence getTombstoneKey() { - return clang::cxindex::RefFileOccurence((const clang::FileEntry *)~0, - (const clang::Decl *)~0); - } - - static unsigned getHashValue(clang::cxindex::RefFileOccurence S) { - typedef std::pair<const clang::FileEntry *, const clang::Decl *> PairTy; - return DenseMapInfo<PairTy>::getHashValue(PairTy(S.File, S.Dcl)); - } - - static bool isEqual(clang::cxindex::RefFileOccurence LHS, - clang::cxindex::RefFileOccurence RHS) { - return LHS.File == RHS.File && LHS.Dcl == RHS.Dcl; - } - }; -} diff --git a/tools/libclang/Makefile b/tools/libclang/Makefile index f33f345..43ecbd1 100644 --- a/tools/libclang/Makefile +++ b/tools/libclang/Makefile @@ -16,21 +16,21 @@ LINK_LIBS_IN_SHARED = 1 SHARED_LIBRARY = 1 include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc -USEDLIBS = clangFrontend.a clangDriver.a \ +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option +USEDLIBS = clangIndex.a clangFrontend.a clangDriver.a \ clangTooling.a \ clangSerialization.a \ clangParse.a clangSema.a \ clangARCMigrate.a clangRewriteFrontend.a clangRewriteCore.a \ clangAnalysis.a clangEdit.a \ clangAST.a clangLex.a clangBasic.a \ - clangFormat.a + clangFormat.a include $(CLANG_LEVEL)/Makefile # Add soname to the library. -ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux FreeBSD GNU)) - LDFLAGS += -Wl,-soname,lib$(LIBRARYNAME)$(SHLIBEXT) +ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux FreeBSD GNU GNU/kFreeBSD)) + LLVMLibsOptions += -Wl,-soname,lib$(LIBRARYNAME)$(SHLIBEXT) endif ##===----------------------------------------------------------------------===## diff --git a/tools/libclang/RecursiveASTVisitor.h b/tools/libclang/RecursiveASTVisitor.h index e45545e..3ad5acb 100644 --- a/tools/libclang/RecursiveASTVisitor.h +++ b/tools/libclang/RecursiveASTVisitor.h @@ -27,6 +27,7 @@ #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" +#include "clang/AST/StmtOpenMP.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" @@ -394,6 +395,7 @@ private: // These are helper methods used by more than one Traverse* method. bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL); bool TraverseClassInstantiations(ClassTemplateDecl *D); + bool TraverseVariableInstantiations(VarTemplateDecl *D); bool TraverseFunctionInstantiations(FunctionTemplateDecl *D) ; bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL, unsigned Count); @@ -404,6 +406,13 @@ private: bool TraverseDeclContextHelper(DeclContext *DC); bool TraverseFunctionHelper(FunctionDecl *D); bool TraverseVarHelper(VarDecl *D); + bool TraverseOMPClause(OMPClause *C); +#define OPENMP_CLAUSE(Name, Class) \ + bool Visit##Class(Class *C); +#include "clang/Basic/OpenMPKinds.def" + /// \brief Process clauses with list of variables. + template <typename T> + void VisitOMPClauseList(T *Node); typedef SmallVector<Stmt *, 16> StmtsTy; typedef SmallVector<StmtsTy *, 4> QueuesTy; @@ -502,7 +511,7 @@ bool RecursiveASTVisitor<Derived>::TraverseStmt(Stmt *S) { } } - for (SmallVector<Stmt *, 8>::reverse_iterator + for (SmallVectorImpl<Stmt *>::reverse_iterator RI = StmtsToEnqueu.rbegin(), RE = StmtsToEnqueu.rend(); RI != RE; ++RI) Queue.push_back(*RI); @@ -786,6 +795,10 @@ DEF_TRAVERSE_TYPE(MemberPointerType, { TRY_TO(TraverseType(T->getPointeeType())); }) +DEF_TRAVERSE_TYPE(DecayedType, { + TRY_TO(TraverseType(T->getOriginalType())); + }) + DEF_TRAVERSE_TYPE(ConstantArrayType, { TRY_TO(TraverseType(T->getElementType())); }) @@ -992,6 +1005,10 @@ DEF_TRAVERSE_TYPELOC(MemberPointerType, { TRY_TO(TraverseTypeLoc(TL.getPointeeLoc())); }) +DEF_TRAVERSE_TYPELOC(DecayedType, { + TRY_TO(TraverseTypeLoc(TL.getOriginalLoc())); + }) + template<typename Derived> bool RecursiveASTVisitor<Derived>::TraverseArrayTypeLocHelper(ArrayTypeLoc TL) { // This isn't available for ArrayType, but is for the ArrayTypeLoc. @@ -1405,6 +1422,57 @@ DEF_TRAVERSE_DECL(ClassTemplateDecl, { // it was instantiated, and thus should not be traversed. }) +// A helper method for traversing the implicit instantiations of a +// class template. +template <typename Derived> +bool RecursiveASTVisitor<Derived>::TraverseVariableInstantiations( + VarTemplateDecl *D) { + VarTemplateDecl::spec_iterator end = D->spec_end(); + for (VarTemplateDecl::spec_iterator it = D->spec_begin(); it != end; ++it) { + VarTemplateSpecializationDecl *SD = *it; + + switch (SD->getSpecializationKind()) { + // Visit the implicit instantiations with the requested pattern. + case TSK_Undeclared: + case TSK_ImplicitInstantiation: + TRY_TO(TraverseDecl(SD)); + break; + + // We don't need to do anything on an explicit instantiation + // or explicit specialization because there will be an explicit + // node for it elsewhere. + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + case TSK_ExplicitSpecialization: + break; + } + } + + return true; +} + +DEF_TRAVERSE_DECL( + VarTemplateDecl, + { + VarDecl *TempDecl = D->getTemplatedDecl(); + TRY_TO(TraverseDecl(TempDecl)); + TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); + + // By default, we do not traverse the instantiations of + // variable templates since they do not appear in the user code. The + // following code optionally traverses them. + // + // We only traverse the variable instantiations when we see the canonical + // declaration of the template, to ensure we only visit them once. + if (getDerived().shouldVisitTemplateInstantiations() && + D == D->getCanonicalDecl()) + TRY_TO(TraverseVariableInstantiations(D)); + + // Note that getInstantiatedFromMemberTemplate() is just a link + // from a template instantiation back to the template from which + // it was instantiated, and thus should not be traversed. +}) + // A helper method for traversing the instantiations of a // function while skipping its specializations. template<typename Derived> @@ -1582,7 +1650,8 @@ DEF_TRAVERSE_DECL(ClassTemplatePartialSpecializationDecl, { } // The args that remains unspecialized. TRY_TO(TraverseTemplateArgumentLocsHelper( - D->getTemplateArgsAsWritten(), D->getNumTemplateArgsAsWritten())); + D->getTemplateArgsAsWritten()->getTemplateArgs(), + D->getTemplateArgsAsWritten()->NumTemplateArgs)); // Don't need the ClassTemplatePartialSpecializationHelper, even // though that's our parent class -- we already visit all the @@ -1731,6 +1800,44 @@ DEF_TRAVERSE_DECL(VarDecl, { TRY_TO(TraverseVarHelper(D)); }) +DEF_TRAVERSE_DECL(VarTemplateSpecializationDecl, { + // For implicit instantiations, we don't want to + // recurse at all, since the instatiated class isn't written in + // the source code anywhere. + if (TypeSourceInfo *TSI = D->getTypeAsWritten()) + TRY_TO(TraverseTypeLoc(TSI->getTypeLoc())); + + if (!getDerived().shouldVisitTemplateInstantiations() && + D->getTemplateSpecializationKind() != TSK_ExplicitSpecialization) + // Returning from here skips traversing the + // declaration context of the VarTemplateSpecializationDecl + // (embedded in the DEF_TRAVERSE_DECL() macro). + return true; +}) + +DEF_TRAVERSE_DECL(VarTemplatePartialSpecializationDecl, + { + // The partial specialization. + if (TemplateParameterList *TPL = D->getTemplateParameters()) { + for (TemplateParameterList::iterator I = TPL->begin(), E = TPL->end(); + I != E; ++I) { + TRY_TO(TraverseDecl(*I)); + } + } + // The args that remains unspecialized. + TRY_TO(TraverseTemplateArgumentLocsHelper( + D->getTemplateArgsAsWritten()->getTemplateArgs(), + D->getTemplateArgsAsWritten()->NumTemplateArgs)); + + // Don't need the VarTemplatePartialSpecializationHelper, even + // though that's our parent class -- we already visit all the + // template args here. + TRY_TO(TraverseVarHelper(D)); + + // Instantiations will have been visited with the primary + // template. +}) + DEF_TRAVERSE_DECL(ImplicitParamDecl, { TRY_TO(TraverseVarHelper(D)); }) @@ -2039,6 +2146,8 @@ DEF_TRAVERSE_STMT(CXXTemporaryObjectExpr, { // Walk only the visible parts of lambda expressions. template<typename Derived> bool RecursiveASTVisitor<Derived>::TraverseLambdaExpr(LambdaExpr *S) { + TRY_TO(WalkUpFromLambdaExpr(S)); + for (LambdaExpr::capture_iterator C = S->explicit_capture_begin(), CEnd = S->explicit_capture_end(); C != CEnd; ++C) { @@ -2095,6 +2204,7 @@ DEF_TRAVERSE_STMT(CXXDefaultInitExpr, { }) DEF_TRAVERSE_STMT(CXXDeleteExpr, { }) DEF_TRAVERSE_STMT(ExprWithCleanups, { }) DEF_TRAVERSE_STMT(CXXNullPtrLiteralExpr, { }) +DEF_TRAVERSE_STMT(CXXStdInitializerListExpr, { }) DEF_TRAVERSE_STMT(CXXPseudoDestructorExpr, { TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc())); if (TypeSourceInfo *ScopeInfo = S->getScopeTypeInfo()) @@ -2132,6 +2242,7 @@ DEF_TRAVERSE_STMT(ParenExpr, { }) DEF_TRAVERSE_STMT(ParenListExpr, { }) DEF_TRAVERSE_STMT(PredefinedExpr, { }) DEF_TRAVERSE_STMT(ShuffleVectorExpr, { }) +DEF_TRAVERSE_STMT(ConvertVectorExpr, { }) DEF_TRAVERSE_STMT(StmtExpr, { }) DEF_TRAVERSE_STMT(UnresolvedLookupExpr, { TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc())); @@ -2191,6 +2302,61 @@ DEF_TRAVERSE_STMT(ObjCDictionaryLiteral, { }) // Traverse OpenCL: AsType, Convert. DEF_TRAVERSE_STMT(AsTypeExpr, { }) +// OpenMP directives. +DEF_TRAVERSE_STMT(OMPParallelDirective, { + ArrayRef<OMPClause *> Clauses = S->clauses(); + for (ArrayRef<OMPClause *>::iterator I = Clauses.begin(), E = Clauses.end(); + I != E; ++I) + if (!TraverseOMPClause(*I)) return false; +}) + +// OpenMP clauses. +template<typename Derived> +bool RecursiveASTVisitor<Derived>::TraverseOMPClause(OMPClause *C) { + if (!C) return true; + switch (C->getClauseKind()) { +#define OPENMP_CLAUSE(Name, Class) \ + case OMPC_##Name: \ + return getDerived().Visit##Class(static_cast<Class*>(C)); +#include "clang/Basic/OpenMPKinds.def" + default: break; + } + return true; +} + +template<typename Derived> +bool RecursiveASTVisitor<Derived>::VisitOMPDefaultClause(OMPDefaultClause *C) { + return true; +} + +template<typename Derived> +template<typename T> +void RecursiveASTVisitor<Derived>::VisitOMPClauseList(T *Node) { + for (typename T::varlist_iterator I = Node->varlist_begin(), + E = Node->varlist_end(); + I != E; ++I) + TraverseStmt(*I); +} + +template<typename Derived> +bool RecursiveASTVisitor<Derived>::VisitOMPPrivateClause(OMPPrivateClause *C) { + VisitOMPClauseList(C); + return true; +} + +template<typename Derived> +bool RecursiveASTVisitor<Derived>::VisitOMPFirstprivateClause( + OMPFirstprivateClause *C) { + VisitOMPClauseList(C); + return true; +} + +template<typename Derived> +bool RecursiveASTVisitor<Derived>::VisitOMPSharedClause(OMPSharedClause *C) { + VisitOMPClauseList(C); + return true; +} + // FIXME: look at the following tricky-seeming exprs to see if we // need to recurse on anything. These are ones that have methods // returning decls or qualtypes or nestednamespecifier -- though I'm diff --git a/tools/libclang/SimpleFormatContext.h b/tools/libclang/SimpleFormatContext.h deleted file mode 100644 index 016d0b6..0000000 --- a/tools/libclang/SimpleFormatContext.h +++ /dev/null @@ -1,75 +0,0 @@ -//===--- SimpleFormatContext.h ----------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -/// \file -/// -/// \brief Defines a utility class for use of clang-format in libclang -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_SIMPLE_FORM_CONTEXT_H -#define LLVM_CLANG_SIMPLE_FORM_CONTEXT_H - -#include "clang/Basic/Diagnostic.h" -#include "clang/Basic/DiagnosticOptions.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/LangOptions.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Rewrite/Core/Rewriter.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/raw_ostream.h" - -namespace clang { - -/// \brief A small class to be used by libclang clients to format -/// a declaration string in memory. This object is instantiated once -/// and used each time a formatting is needed. -class SimpleFormatContext { -public: - SimpleFormatContext(LangOptions Options) - : DiagOpts(new DiagnosticOptions()), - Diagnostics(new DiagnosticsEngine(new DiagnosticIDs, - DiagOpts.getPtr())), - Files((FileSystemOptions())), - Sources(*Diagnostics, Files), - Rewrite(Sources, Options) { - Diagnostics->setClient(new IgnoringDiagConsumer, true); - } - - ~SimpleFormatContext() { } - - FileID createInMemoryFile(StringRef Name, StringRef Content) { - const llvm::MemoryBuffer *Source = - llvm::MemoryBuffer::getMemBuffer(Content); - const FileEntry *Entry = - Files.getVirtualFile(Name, Source->getBufferSize(), 0); - Sources.overrideFileContents(Entry, Source, true); - assert(Entry != NULL); - return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User); - } - - std::string getRewrittenText(FileID ID) { - std::string Result; - llvm::raw_string_ostream OS(Result); - Rewrite.getEditBuffer(ID).write(OS); - OS.flush(); - return Result; - } - - IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; - IntrusiveRefCntPtr<DiagnosticsEngine> Diagnostics; - FileManager Files; - SourceManager Sources; - Rewriter Rewrite; -}; - -} // end namespace clang - -#endif diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index 0c9912e..9bf26c9 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -2,6 +2,7 @@ clang_CXCursorSet_contains clang_CXCursorSet_insert clang_CXIndex_getGlobalOptions clang_CXIndex_setGlobalOptions +clang_CXXMethod_isPureVirtual clang_CXXMethod_isStatic clang_CXXMethod_isVirtual clang_Cursor_getArgument @@ -19,6 +20,7 @@ clang_Cursor_getReceiverType clang_Cursor_isBitField clang_Cursor_isDynamicCall clang_Cursor_isNull +clang_Cursor_isObjCOptional clang_Cursor_isVariadic clang_Cursor_getModule clang_Module_getASTFile @@ -59,8 +61,10 @@ clang_TParamCommandComment_isParamPositionValid clang_TParamCommandComment_getDepth clang_TParamCommandComment_getIndex clang_Type_getAlignOf +clang_Type_getClassType clang_Type_getSizeOf clang_Type_getOffsetOf +clang_Type_getCXXRefQualifier clang_VerbatimBlockLineComment_getText clang_VerbatimLineComment_getText clang_HTMLTagComment_getAsString @@ -253,6 +257,7 @@ clang_isVirtualBase clang_isVolatileQualifiedType clang_loadDiagnostics clang_Location_isInSystemHeader +clang_Location_isFromMainFile clang_parseTranslationUnit clang_remap_dispose clang_remap_getFilenames diff --git a/tools/scan-build/ccc-analyzer b/tools/scan-build/ccc-analyzer index 60b0185..b463ec0 100755 --- a/tools/scan-build/ccc-analyzer +++ b/tools/scan-build/ccc-analyzer @@ -325,11 +325,6 @@ sub Analyze { my %CompileOptionMap = ( '-nostdinc' => 0, - '-fblocks' => 0, - '-fno-builtin' => 0, - '-fobjc-gc-only' => 0, - '-fobjc-gc' => 0, - '-ffreestanding' => 0, '-include' => 1, '-idirafter' => 1, '-imacros' => 1, @@ -346,18 +341,16 @@ my %LinkerOptionMap = ( ); my %CompilerLinkerOptionMap = ( - '-fobjc-arc' => 0, - '-fno-objc-arc' => 0, - '-fobjc-abi-version' => 0, # This is really a 1 argument, but always has '=' - '-fobjc-legacy-dispatch' => 0, + '-Wwrite-strings' => 0, + '-ftrapv-handler' => 1, # specifically call out separated -f flag '-mios-simulator-version-min' => 0, # This really has 1 argument, but always has '=' '-isysroot' => 1, '-arch' => 1, '-m32' => 0, '-m64' => 0, '-stdlib' => 0, # This is really a 1 argument, but always has '=' + '-target' => 1, '-v' => 0, - '-fpascal-strings' => 0, '-mmacosx-version-min' => 0, # This is really a 1 argument, but always has '=' '-miphoneos-version-min' => 0 # This is really a 1 argument, but always has '=' ); @@ -427,8 +420,8 @@ my %Uniqued; # Forward arguments to gcc. my $Status = system($Compiler,@ARGV); -if (defined $ENV{'CCC_ANALYZER_LOG'}) { - print "$Compiler @ARGV\n"; +if (defined $ENV{'CCC_ANALYZER_LOG'}) { + print STDERR "$Compiler @ARGV\n"; } if ($Status) { exit($Status >> 8); } @@ -453,8 +446,8 @@ if (!defined $OutputFormat) { $OutputFormat = "html"; } # Determine the level of verbosity. my $Verbose = 0; -if (defined $ENV{CCC_ANALYZER_VERBOSE}) { $Verbose = 1; } -if (defined $ENV{CCC_ANALYZER_LOG}) { $Verbose = 2; } +if (defined $ENV{'CCC_ANALYZER_VERBOSE'}) { $Verbose = 1; } +if (defined $ENV{'CCC_ANALYZER_LOG'}) { $Verbose = 2; } # Get the HTML output directory. my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'}; @@ -491,10 +484,6 @@ foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; } next; } - if ($Arg =~ /-m.*/) { - push @CompileOpts,$Arg; - next; - } # Handle the case where there isn't a space after -iquote if ($Arg =~ /-iquote.*/) { push @CompileOpts,$Arg; @@ -556,6 +545,11 @@ foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { next; } + if ($Arg =~ /-m.*/) { + push @CompileOpts,$Arg; + next; + } + # Language. if ($Arg eq '-x') { $Lang = $ARGV[$i+1]; @@ -574,6 +568,9 @@ foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { if ($Arg eq '-O') { push @LinkOpts,'-O1'; } elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; } else { push @LinkOpts,$Arg; } + + # Must pass this along for the __OPTIMIZE__ macro + if ($Arg =~ /^-O/) { push @CompileOpts,$Arg; } next; } @@ -582,12 +579,6 @@ foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { next; } -# if ($Arg =~ /^-f/) { -# # FIXME: Not sure if the remaining -fxxxx options have no arguments. -# push @CompileOpts,$Arg; -# push @LinkOpts,$Arg; # FIXME: Not sure if these are link opts. -# } - # Get the compiler/link mode. if ($Arg =~ /^-F(.+)$/) { my $Tmp = $Arg; @@ -611,6 +602,12 @@ foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { next; } + if ($Arg =~ /^-f/) { + push @CompileOpts,$Arg; + push @LinkOpts,$Arg; + next; + } + # Handle -Wno-. We don't care about extra warnings, but # we should suppress ones that we don't want to see. if ($Arg =~ /^-Wno-/) { diff --git a/tools/scan-build/scan-build b/tools/scan-build/scan-build index 35f852e..0f119f6 100755 --- a/tools/scan-build/scan-build +++ b/tools/scan-build/scan-build @@ -32,7 +32,9 @@ my $TERM = $ENV{'TERM'}; my $UseColor = (defined $TERM and $TERM =~ 'xterm-.*color' and -t STDOUT and defined $ENV{'SCAN_BUILD_COLOR'}); -my $UserName = HtmlEscape(getpwuid($<) || 'unknown'); +# Portability: getpwuid is not implemented for Win32 (see Perl language +# reference, perlport), use getlogin instead. +my $UserName = HtmlEscape(getlogin() || getpwuid($<) || 'unknown'); my $HostName = HtmlEscape(hostname() || 'unknown'); my $CurrentDir = HtmlEscape(getcwd()); my $CurrentDirSuffix = basename($CurrentDir); @@ -121,8 +123,7 @@ sub GetHTMLRunDir { my $Dir = shift @_; my $TmpMode = 0; if (!defined $Dir) { - $Dir = $ENV{'TMPDIR'}; - if (!defined $Dir) { $Dir = "/tmp"; } + $Dir = $ENV{'TMPDIR'} || $ENV{'TEMP'} || $ENV{'TMP'} || "/tmp"; $TmpMode = 1; } @@ -134,7 +135,13 @@ sub GetHTMLRunDir { my $year = $CurrentTime[5] + 1900; my $day = $CurrentTime[3]; my $month = $CurrentTime[4] + 1; - my $DateString = sprintf("%d-%02d-%02d", $year, $month, $day); + my $hour = $CurrentTime[2]; + my $min = $CurrentTime[1]; + my $sec = $CurrentTime[0]; + + my $TimeString = sprintf("%02d%02d%02d", $hour, $min, $sec); + my $DateString = sprintf("%d-%02d-%02d-%s-$$", + $year, $month, $day, $TimeString); # Determine the run number. my $RunNumber; @@ -161,9 +168,11 @@ sub GetHTMLRunDir { next if ($x[0] != $year); next if ($x[1] != $month); next if ($x[2] != $day); + next if ($x[3] != $TimeString); + next if ($x[4] != $$); - if ($x[3] > $max) { - $max = $x[3]; + if ($x[5] > $max) { + $max = $x[5]; } } @@ -896,6 +905,9 @@ sub SetEnv { } } +# The flag corresponding to the --override-compiler command line option. +my $OverrideCompiler = 0; + sub RunXcodebuild { my $Args = shift; my $IgnoreErrors = shift; @@ -910,7 +922,7 @@ sub RunXcodebuild { # Detect the version of Xcode. If Xcode 4.6 or higher, use new # in situ support for analyzer interposition without needed to override # the compiler. - open(DETECT_XCODE, "xcodebuild -version |") or + open(DETECT_XCODE, "-|", $Args->[0], "-version") or die "error: cannot detect version of xcodebuild\n"; my $oldBehavior = 1; @@ -928,6 +940,12 @@ sub RunXcodebuild { } close(DETECT_XCODE); + # If --override-compiler is explicitely requested, resort to the old + # behavior regardless of Xcode version. + if ($OverrideCompiler) { + $oldBehavior = 1; + } + if ($oldBehavior == 0) { my $OutputDir = $Options->{"OUTPUT_DIR"}; my $CLANG = $Options->{"CLANG"}; @@ -976,16 +994,11 @@ sub RunBuildCommand { my $CCAnalyzer = shift; my $CXXAnalyzer = shift; my $Options = shift; - - # Get only the part of the command after the last '/'. - if ($Cmd =~ /\/([^\/]+)$/) { - $Cmd = $1; - } - - if ($Cmd eq "xcodebuild") { + + if ($Cmd =~ /\bxcodebuild$/) { return RunXcodebuild($Args, $IgnoreErrors, $CCAnalyzer, $CXXAnalyzer, $Options); } - + # Setup the environment. SetEnv($Options); @@ -1013,10 +1026,10 @@ sub RunBuildCommand { shift @$Args; unshift @$Args, $CXXAnalyzer; } - elsif ($IgnoreErrors) { - if ($Cmd eq "make" or $Cmd eq "gmake") { - AddIfNotPresent($Args, "CC=$CCAnalyzer"); - AddIfNotPresent($Args, "CXX=$CXXAnalyzer"); + elsif ($Cmd eq "make" or $Cmd eq "gmake") { + AddIfNotPresent($Args, "CC=$CCAnalyzer"); + AddIfNotPresent($Args, "CXX=$CXXAnalyzer"); + if ($IgnoreErrors) { AddIfNotPresent($Args,"-k"); AddIfNotPresent($Args,"-i"); } @@ -1148,6 +1161,10 @@ ADVANCED OPTIONS: Don't remove the build results directory even if no issues were reported. + --override-compiler + Always resort to the ccc-analyzer even when better interposition methods + are available. + CONTROLLING CHECKERS: A default group of checkers are always run unless explicitly disabled. @@ -1511,6 +1528,12 @@ while (@ARGV) { $KeepEmpty = 1; next; } + + if ($arg eq "--override-compiler") { + shift @ARGV; + $OverrideCompiler = 1; + next; + } DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); @@ -1589,13 +1612,17 @@ my $AbsRealBin = Cwd::realpath($RealBin); my $Cmd = "$AbsRealBin/libexec/ccc-analyzer"; my $CmdCXX = "$AbsRealBin/libexec/c++-analyzer"; -if (!defined $Cmd || ! -x $Cmd) { +# Portability: use less strict but portable check -e (file exists) instead of +# non-portable -x (file is executable). On some windows ports -x just checks +# file extension to determine if a file is executable (see Perl language +# reference, perlport) +if (!defined $Cmd || ! -e $Cmd) { $Cmd = "$AbsRealBin/ccc-analyzer"; - DieDiag("Executable 'ccc-analyzer' does not exist at '$Cmd'\n") if(! -x $Cmd); + DieDiag("'ccc-analyzer' does not exist at '$Cmd'\n") if(! -e $Cmd); } -if (!defined $CmdCXX || ! -x $CmdCXX) { +if (!defined $CmdCXX || ! -e $CmdCXX) { $CmdCXX = "$AbsRealBin/c++-analyzer"; - DieDiag("Executable 'c++-analyzer' does not exist at '$CmdCXX'\n") if(! -x $CmdCXX); + DieDiag("'c++-analyzer' does not exist at '$CmdCXX'\n") if(! -e $CmdCXX); } Diag("Using '$Clang' for static analysis\n"); |