[clang] [clang-tools-extra] [llvm] [clang-format] Add support for BasedOnStyle referencing an arbitrary file (PR #107312)
https://github.com/jediry created https://github.com/llvm/llvm-project/pull/107312 At present, the ```BasedOnStyle``` directive can reference a predefined style name, or ```InheritFromParent```, instructing clang-format to search upward in the directory hierarchy for a .clang-format file. This works fine when variations in codebase formatting align with the directory hierarchy, but becomes problematic when it is desired to share formatting across portions of a repository with no common parent directory. For example, consider the case of a large, multi-team repository containing many projects as sub-directories of the repository root, where each of the various engineering teams owns multiple of these projects, and wants a common coding style across all of its owned projects. In this PR, I'm extending ```BasedOnStyle``` to allow referencing an arbitrary file. While this could be an absolute path, that's not typically useful across machines. In typical usage, this will be a relative path (e.g., ```BasedOnStyle: format/team1.clang-format```). For resolving relative paths, I've mimicked the "include path" model of C/C++ (and, in fact, I'm able to leverage ```SourceMgr```'s "include paths" mechanism to implement this). The list of style search paths is specified on the command-line via one or more ```--style-search-path ``` parameters. The interesting stuff is in Format.cpp...most of the rest of this change is plumbing to get the StyleSearchPaths from the command-line to the call to ```getStyle()```. Unfinished aspects of this change: * No unit tests. I am happy to write some, but I'm unsure how to do this in a harmonious way, seeing as my change inherently relies on external files on the filesystem. Most of the unit tests I've seen in Clang rely on in-code strings. * ```--style-search-path .``` does not seem to work for specifying the current directory as a search path. Evidently the ```SourceMgr``` include paths mechanism does not handle this natively. Can someone point me to the proper "Clang" way to resolve paths like . or .. into paths that ```SourceMgr``` can handle? >From 163657095741b2292540fce7ff424cb4600391da Mon Sep 17 00:00:00 2001 From: Ryan Saunders Date: Fri, 2 Aug 2024 15:08:24 -0700 Subject: [PATCH] Add support for BasedOnStyle referencing an arbitrary file, locatable relative to directories specified via --style-search-path Improve command-line help Clean up formatting Fix unit tests Fix other clang tools that leverage format::getStyle() Fix up clangd --- .../tool/ClangApplyReplacementsMain.cpp | 19 .../ChangeNamespace.cpp | 7 +- .../clang-change-namespace/ChangeNamespace.h | 6 +- .../tool/ClangChangeNamespace.cpp | 18 ++- .../tool/ClangIncludeFixer.cpp| 22 +++- clang-tools-extra/clang-move/Move.cpp | 1 + clang-tools-extra/clang-move/Move.h | 3 + .../clang-move/tool/ClangMove.cpp | 22 +++- clang-tools-extra/clang-tidy/ClangTidy.cpp| 3 +- .../clang-tidy/ClangTidyOptions.h | 4 + .../clang-tidy/misc/IncludeCleanerCheck.cpp | 5 +- .../clang-tidy/misc/IncludeCleanerCheck.h | 1 + .../clang-tidy/tool/ClangTidyMain.cpp | 15 +++ clang-tools-extra/clangd/ClangdLSPServer.cpp | 2 +- clang-tools-extra/clangd/ClangdServer.cpp | 13 ++- clang-tools-extra/clangd/ClangdServer.h | 11 +- clang-tools-extra/clangd/Config.h | 4 + clang-tools-extra/clangd/IncludeCleaner.cpp | 1 + clang-tools-extra/clangd/SourceCode.cpp | 3 + clang-tools-extra/clangd/tool/Check.cpp | 2 +- clang-tools-extra/clangd/tool/ClangdMain.cpp | 23 .../clangd/unittests/ClangdTests.cpp | 2 +- .../include-cleaner/tool/IncludeCleaner.cpp | 15 ++- .../ChangeNamespaceTests.cpp | 2 +- .../unittests/clang-move/ClangMoveTests.cpp | 4 +- clang/include/clang/Format/Format.h | 29 - clang/include/clang/Tooling/Refactoring.h | 4 +- clang/lib/Format/Format.cpp | 51 +++-- clang/lib/Tooling/Refactoring.cpp | 4 +- clang/tools/clang-format/ClangFormat.cpp | 21 +++- clang/unittests/Format/ConfigParseTest.cpp| 106 ++ clang/unittests/Format/FormatTestObjC.cpp | 56 - .../unittests/Format/FormatTestRawStrings.cpp | 2 +- .../ObjCPropertyAttributeOrderFixerTest.cpp | 6 +- clang/unittests/Format/QualifierFixerTest.cpp | 6 +- clang/unittests/Rename/ClangRenameTest.h | 2 +- clang/unittests/Tooling/RefactoringTest.cpp | 3 +- llvm/include/llvm/Support/YAMLTraits.h| 6 + llvm/lib/Support/YAMLTraits.cpp | 12 ++ 39 files changed, 391 insertions(+), 125 deletions(-) diff --git a/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp b/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMa
[clang] [clang-tools-extra] [llvm] [clang-format] Add support for BasedOnStyle referencing an arbitrary file (PR #107312)
@@ -5274,9 +5274,17 @@ struct FormatStyle { friend std::error_code parseConfiguration(llvm::MemoryBufferRef Config, FormatStyle *Style, + const std::vector &StyleSearchPaths, jediry wrote: Yes, I can certainly do that if that is preferred. And, if I'm to do this as multiple PRs as several have requested, then a default param (or an overload) is needed. FWIW, though, I did this intentionally, to flush out all callers of lib/format: once this feature is in and used by actual users, this needs to be pushed through all the tools that leverage lib/format, or else those tools will choke on peoples' valid .clang-format files. https://github.com/llvm/llvm-project/pull/107312 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [llvm] [clang-format] Add support for BasedOnStyle referencing an arbitrary file (PR #107312)
@@ -39,6 +39,10 @@ IO::IO(void *Context) : Ctxt(Context) {} IO::~IO() = default; +SourceMgr& IO::getSourceMgr() { jediry wrote: Sure, I can break this up. https://github.com/llvm/llvm-project/pull/107312 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [llvm] [clang-format] Add support for BasedOnStyle referencing an arbitrary file (PR #107312)
jediry wrote: > I am not sure if it's best to push a new command line flag to all other tools > that use clang-format as a library. > > Have you considered any other alternatives that can self-contain this in > clang-format? e.g can we just treat paths as relative to current > `.clang-format` file ? @kadircet @mydeveloperday @HazardyKnusperkeks I would love to make this more self-contained, as this certainly does hit a lot of layers of code. I specifically selected this approach because of its similarity to the familiar ```-I``` mechanism for specifying include paths for C/C++. Two other ideas I did consider are: 1. Allow the use of environment variables in the path (e.g., ```BasedOnStyle: $(FORMATDIR)\team1.clang-format```). Not sure if this is better or worse than the current approach, though it's certainly a smaller code change. (That said, I find that most command-line tools that find things via environment variable also support command-line overrides for this, since it's sometimes cumbersome to have to set the environment variable in order to use the tool.) 2. As you say, do this relative to the current .clang-format file. This is also more self-contained, though it has the downside of being easily broken by code moves (e.g., if I "git mv" a directory to a different path, expressions like ```BasedOnStyle: ../../../format/team1.clang-format``` become invalid). I'm happy to go a different way with this if that is preferred. https://github.com/llvm/llvm-project/pull/107312 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [llvm] [clang-format] Add support for BasedOnStyle referencing an arbitrary file (PR #107312)
jediry wrote: @owenca I've opened an issue [here](https://github.com/llvm/llvm-project/issues/107808) https://github.com/llvm/llvm-project/pull/107312 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Support BasedOnStyle referencing an arbitrary file (PR #110634)
https://github.com/jediry updated https://github.com/llvm/llvm-project/pull/110634 >From 82160d1e6de3f197c847bf8ed21ea1fc314b3cf4 Mon Sep 17 00:00:00 2001 From: Ryan Saunders Date: Tue, 1 Oct 2024 00:03:50 -0700 Subject: [PATCH 1/2] Support BasedOnStyle: file:blah.clang-format --- clang/include/clang/Format/Format.h | 22 ++- clang/lib/Format/Format.cpp | 207 +--- 2 files changed, 174 insertions(+), 55 deletions(-) diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index d8b62c7652a0f6..1b833256500431 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -53,10 +53,24 @@ std::error_code make_error_code(ParseError e); /// The ``FormatStyle`` is used to configure the formatting to follow /// specific guidelines. struct FormatStyle { - // If the BasedOn: was InheritParentConfig and this style needs the file from - // the parent directories. It is not part of the actual style for formatting. - // Thus the // instead of ///. - bool InheritsParentConfig; + // If the BasedOnStyle: was InheritParentConfig, this is the string + // "", indicating to search upwards until a _clang-format or + // .clang-format file is found. + // + // Else, if the BasedOnStyle: was an explicit "file:" reference, this is + // that reference, verbatim, including the "file:" prefix. The string + // after "file:" may start with $(CLANG_FORMAT_DIR), in which case the value + // of the CLANG_FORMAT_DIR environment variable (which must be defined) is + // substituted; otherwise, the string after "file:" is interpreted as a + // path relative to the current config file. (Absolute paths are not + // permitted, for security reasons.) + // + // Else (i.e., if the BasedOnStyle is omitted or a predefined style), this is + // the empty string. + // + // This field is not part of the actual style for formatting, thus the // + // instead of ///. + std::string InheritsConfig; /// The extra indent or outdent of access modifiers, e.g. ``public:``. /// \version 3.3 diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index d2463b892fbb96..bfca01d1a67230 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1543,7 +1543,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.IndentRequiresClause = true; LLVMStyle.IndentWidth = 2; LLVMStyle.IndentWrappedFunctionNames = false; - LLVMStyle.InheritsParentConfig = false; + LLVMStyle.InheritsConfig.clear(); LLVMStyle.InsertBraces = false; LLVMStyle.InsertNewlineAtEOF = false; LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None; @@ -1992,7 +1992,9 @@ bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, else if (Name.equals_insensitive("none")) *Style = getNoStyle(); else if (Name.equals_insensitive("inheritparentconfig")) -Style->InheritsParentConfig = true; +Style->InheritsConfig = ""; + else if (Name.starts_with_insensitive("file:")) +Style->InheritsConfig = Name; else return false; @@ -4004,6 +4006,45 @@ loadAndParseConfigFile(StringRef ConfigFile, llvm::vfs::FileSystem *FS, return Text; } +// Resolves an explicit file: reference in a BasedOnStyle directive to a +// canonical, absolute path, substituting the value of the CLANG_FORMAT_DIR +// environment variable if the path starts with $(CLANG_FORMAT_DIR), or treating +// BasedOnFile as relative to the current ConfigFile otherwise. +static Expected> +resolveExplicitParentConfigFile(StringRef ConfigFile, StringRef BasedOnFile, +llvm::vfs::FileSystem *FS) { + constexpr const char *EnvVar = "CLANG_FORMAT_DIR"; + constexpr const char *EnvVarExpansion = "$(CLANG_FORMAT_DIR)"; + if (BasedOnFile.starts_with(EnvVarExpansion)) { +const char *ClangFormatDir = getenv(EnvVar); +if (ClangFormatDir == nullptr) { + return make_string_error(ConfigFile + ": 'BasedOnStyle: " + BasedOnFile + + "' uses " + EnvVarExpansion + ", but the " + + EnvVar + " environment variable is not defined"); +} else { + auto Status = FS->status(ClangFormatDir); + if (!Status || + Status->getType() != llvm::sys::fs::file_type::directory_file) { +return make_string_error( +StringRef("Environment variable ") + EnvVar + " = " + +ClangFormatDir + "does not refer to a valid, readable directory"); + } + + SmallString<128> FileName{ClangFormatDir}; + llvm::sys::path::append(FileName, + BasedOnFile.substr(strlen(EnvVarExpansion))); + ; + llvm::sys::path::remove_dots(FileName, true /*remove_dot_dot*/); + return FileName; +} + } else { +SmallString<128> FileName = llvm::sys::path::parent_path(ConfigFile); +llvm::sys::path::append(FileName, BasedOnFile); +llvm::sys::path::re
[clang] Support BasedOnStyle referencing an arbitrary file (PR #110634)
https://github.com/jediry updated https://github.com/llvm/llvm-project/pull/110634 >From 82160d1e6de3f197c847bf8ed21ea1fc314b3cf4 Mon Sep 17 00:00:00 2001 From: Ryan Saunders Date: Tue, 1 Oct 2024 00:03:50 -0700 Subject: [PATCH 1/2] Support BasedOnStyle: file:blah.clang-format --- clang/include/clang/Format/Format.h | 22 ++- clang/lib/Format/Format.cpp | 207 +--- 2 files changed, 174 insertions(+), 55 deletions(-) diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index d8b62c7652a0f6..1b833256500431 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -53,10 +53,24 @@ std::error_code make_error_code(ParseError e); /// The ``FormatStyle`` is used to configure the formatting to follow /// specific guidelines. struct FormatStyle { - // If the BasedOn: was InheritParentConfig and this style needs the file from - // the parent directories. It is not part of the actual style for formatting. - // Thus the // instead of ///. - bool InheritsParentConfig; + // If the BasedOnStyle: was InheritParentConfig, this is the string + // "", indicating to search upwards until a _clang-format or + // .clang-format file is found. + // + // Else, if the BasedOnStyle: was an explicit "file:" reference, this is + // that reference, verbatim, including the "file:" prefix. The string + // after "file:" may start with $(CLANG_FORMAT_DIR), in which case the value + // of the CLANG_FORMAT_DIR environment variable (which must be defined) is + // substituted; otherwise, the string after "file:" is interpreted as a + // path relative to the current config file. (Absolute paths are not + // permitted, for security reasons.) + // + // Else (i.e., if the BasedOnStyle is omitted or a predefined style), this is + // the empty string. + // + // This field is not part of the actual style for formatting, thus the // + // instead of ///. + std::string InheritsConfig; /// The extra indent or outdent of access modifiers, e.g. ``public:``. /// \version 3.3 diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index d2463b892fbb96..bfca01d1a67230 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1543,7 +1543,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.IndentRequiresClause = true; LLVMStyle.IndentWidth = 2; LLVMStyle.IndentWrappedFunctionNames = false; - LLVMStyle.InheritsParentConfig = false; + LLVMStyle.InheritsConfig.clear(); LLVMStyle.InsertBraces = false; LLVMStyle.InsertNewlineAtEOF = false; LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None; @@ -1992,7 +1992,9 @@ bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, else if (Name.equals_insensitive("none")) *Style = getNoStyle(); else if (Name.equals_insensitive("inheritparentconfig")) -Style->InheritsParentConfig = true; +Style->InheritsConfig = ""; + else if (Name.starts_with_insensitive("file:")) +Style->InheritsConfig = Name; else return false; @@ -4004,6 +4006,45 @@ loadAndParseConfigFile(StringRef ConfigFile, llvm::vfs::FileSystem *FS, return Text; } +// Resolves an explicit file: reference in a BasedOnStyle directive to a +// canonical, absolute path, substituting the value of the CLANG_FORMAT_DIR +// environment variable if the path starts with $(CLANG_FORMAT_DIR), or treating +// BasedOnFile as relative to the current ConfigFile otherwise. +static Expected> +resolveExplicitParentConfigFile(StringRef ConfigFile, StringRef BasedOnFile, +llvm::vfs::FileSystem *FS) { + constexpr const char *EnvVar = "CLANG_FORMAT_DIR"; + constexpr const char *EnvVarExpansion = "$(CLANG_FORMAT_DIR)"; + if (BasedOnFile.starts_with(EnvVarExpansion)) { +const char *ClangFormatDir = getenv(EnvVar); +if (ClangFormatDir == nullptr) { + return make_string_error(ConfigFile + ": 'BasedOnStyle: " + BasedOnFile + + "' uses " + EnvVarExpansion + ", but the " + + EnvVar + " environment variable is not defined"); +} else { + auto Status = FS->status(ClangFormatDir); + if (!Status || + Status->getType() != llvm::sys::fs::file_type::directory_file) { +return make_string_error( +StringRef("Environment variable ") + EnvVar + " = " + +ClangFormatDir + "does not refer to a valid, readable directory"); + } + + SmallString<128> FileName{ClangFormatDir}; + llvm::sys::path::append(FileName, + BasedOnFile.substr(strlen(EnvVarExpansion))); + ; + llvm::sys::path::remove_dots(FileName, true /*remove_dot_dot*/); + return FileName; +} + } else { +SmallString<128> FileName = llvm::sys::path::parent_path(ConfigFile); +llvm::sys::path::append(FileName, BasedOnFile); +llvm::sys::path::re
[clang] Support BasedOnStyle referencing an arbitrary file (PR #110634)
jediry wrote: > Can this have some unit test? Done. Added docs as well. https://github.com/llvm/llvm-project/pull/110634 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Support BasedOnStyle referencing an arbitrary file (PR #110634)
https://github.com/jediry updated https://github.com/llvm/llvm-project/pull/110634 >From 82160d1e6de3f197c847bf8ed21ea1fc314b3cf4 Mon Sep 17 00:00:00 2001 From: Ryan Saunders Date: Tue, 1 Oct 2024 00:03:50 -0700 Subject: [PATCH 1/3] Support BasedOnStyle: file:blah.clang-format --- clang/include/clang/Format/Format.h | 22 ++- clang/lib/Format/Format.cpp | 207 +--- 2 files changed, 174 insertions(+), 55 deletions(-) diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index d8b62c7652a0f6..1b833256500431 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -53,10 +53,24 @@ std::error_code make_error_code(ParseError e); /// The ``FormatStyle`` is used to configure the formatting to follow /// specific guidelines. struct FormatStyle { - // If the BasedOn: was InheritParentConfig and this style needs the file from - // the parent directories. It is not part of the actual style for formatting. - // Thus the // instead of ///. - bool InheritsParentConfig; + // If the BasedOnStyle: was InheritParentConfig, this is the string + // "", indicating to search upwards until a _clang-format or + // .clang-format file is found. + // + // Else, if the BasedOnStyle: was an explicit "file:" reference, this is + // that reference, verbatim, including the "file:" prefix. The string + // after "file:" may start with $(CLANG_FORMAT_DIR), in which case the value + // of the CLANG_FORMAT_DIR environment variable (which must be defined) is + // substituted; otherwise, the string after "file:" is interpreted as a + // path relative to the current config file. (Absolute paths are not + // permitted, for security reasons.) + // + // Else (i.e., if the BasedOnStyle is omitted or a predefined style), this is + // the empty string. + // + // This field is not part of the actual style for formatting, thus the // + // instead of ///. + std::string InheritsConfig; /// The extra indent or outdent of access modifiers, e.g. ``public:``. /// \version 3.3 diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index d2463b892fbb96..bfca01d1a67230 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1543,7 +1543,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.IndentRequiresClause = true; LLVMStyle.IndentWidth = 2; LLVMStyle.IndentWrappedFunctionNames = false; - LLVMStyle.InheritsParentConfig = false; + LLVMStyle.InheritsConfig.clear(); LLVMStyle.InsertBraces = false; LLVMStyle.InsertNewlineAtEOF = false; LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None; @@ -1992,7 +1992,9 @@ bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, else if (Name.equals_insensitive("none")) *Style = getNoStyle(); else if (Name.equals_insensitive("inheritparentconfig")) -Style->InheritsParentConfig = true; +Style->InheritsConfig = ""; + else if (Name.starts_with_insensitive("file:")) +Style->InheritsConfig = Name; else return false; @@ -4004,6 +4006,45 @@ loadAndParseConfigFile(StringRef ConfigFile, llvm::vfs::FileSystem *FS, return Text; } +// Resolves an explicit file: reference in a BasedOnStyle directive to a +// canonical, absolute path, substituting the value of the CLANG_FORMAT_DIR +// environment variable if the path starts with $(CLANG_FORMAT_DIR), or treating +// BasedOnFile as relative to the current ConfigFile otherwise. +static Expected> +resolveExplicitParentConfigFile(StringRef ConfigFile, StringRef BasedOnFile, +llvm::vfs::FileSystem *FS) { + constexpr const char *EnvVar = "CLANG_FORMAT_DIR"; + constexpr const char *EnvVarExpansion = "$(CLANG_FORMAT_DIR)"; + if (BasedOnFile.starts_with(EnvVarExpansion)) { +const char *ClangFormatDir = getenv(EnvVar); +if (ClangFormatDir == nullptr) { + return make_string_error(ConfigFile + ": 'BasedOnStyle: " + BasedOnFile + + "' uses " + EnvVarExpansion + ", but the " + + EnvVar + " environment variable is not defined"); +} else { + auto Status = FS->status(ClangFormatDir); + if (!Status || + Status->getType() != llvm::sys::fs::file_type::directory_file) { +return make_string_error( +StringRef("Environment variable ") + EnvVar + " = " + +ClangFormatDir + "does not refer to a valid, readable directory"); + } + + SmallString<128> FileName{ClangFormatDir}; + llvm::sys::path::append(FileName, + BasedOnFile.substr(strlen(EnvVarExpansion))); + ; + llvm::sys::path::remove_dots(FileName, true /*remove_dot_dot*/); + return FileName; +} + } else { +SmallString<128> FileName = llvm::sys::path::parent_path(ConfigFile); +llvm::sys::path::append(FileName, BasedOnFile); +llvm::sys::path::re
[clang] Support BasedOnStyle referencing an arbitrary file (PR #110634)
jediry wrote: Ping @mydeveloperday @owenca https://github.com/llvm/llvm-project/pull/110634 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Support BasedOnStyle referencing an arbitrary file (PR #110634)
jediry wrote: Ping https://github.com/llvm/llvm-project/pull/110634 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Support BasedOnStyle referencing an arbitrary file (PR #110634)
https://github.com/jediry updated https://github.com/llvm/llvm-project/pull/110634 >From 82160d1e6de3f197c847bf8ed21ea1fc314b3cf4 Mon Sep 17 00:00:00 2001 From: Ryan Saunders Date: Tue, 1 Oct 2024 00:03:50 -0700 Subject: [PATCH 1/3] Support BasedOnStyle: file:blah.clang-format --- clang/include/clang/Format/Format.h | 22 ++- clang/lib/Format/Format.cpp | 207 +--- 2 files changed, 174 insertions(+), 55 deletions(-) diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index d8b62c7652a0f6..1b833256500431 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -53,10 +53,24 @@ std::error_code make_error_code(ParseError e); /// The ``FormatStyle`` is used to configure the formatting to follow /// specific guidelines. struct FormatStyle { - // If the BasedOn: was InheritParentConfig and this style needs the file from - // the parent directories. It is not part of the actual style for formatting. - // Thus the // instead of ///. - bool InheritsParentConfig; + // If the BasedOnStyle: was InheritParentConfig, this is the string + // "", indicating to search upwards until a _clang-format or + // .clang-format file is found. + // + // Else, if the BasedOnStyle: was an explicit "file:" reference, this is + // that reference, verbatim, including the "file:" prefix. The string + // after "file:" may start with $(CLANG_FORMAT_DIR), in which case the value + // of the CLANG_FORMAT_DIR environment variable (which must be defined) is + // substituted; otherwise, the string after "file:" is interpreted as a + // path relative to the current config file. (Absolute paths are not + // permitted, for security reasons.) + // + // Else (i.e., if the BasedOnStyle is omitted or a predefined style), this is + // the empty string. + // + // This field is not part of the actual style for formatting, thus the // + // instead of ///. + std::string InheritsConfig; /// The extra indent or outdent of access modifiers, e.g. ``public:``. /// \version 3.3 diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index d2463b892fbb96..bfca01d1a67230 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1543,7 +1543,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.IndentRequiresClause = true; LLVMStyle.IndentWidth = 2; LLVMStyle.IndentWrappedFunctionNames = false; - LLVMStyle.InheritsParentConfig = false; + LLVMStyle.InheritsConfig.clear(); LLVMStyle.InsertBraces = false; LLVMStyle.InsertNewlineAtEOF = false; LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None; @@ -1992,7 +1992,9 @@ bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, else if (Name.equals_insensitive("none")) *Style = getNoStyle(); else if (Name.equals_insensitive("inheritparentconfig")) -Style->InheritsParentConfig = true; +Style->InheritsConfig = ""; + else if (Name.starts_with_insensitive("file:")) +Style->InheritsConfig = Name; else return false; @@ -4004,6 +4006,45 @@ loadAndParseConfigFile(StringRef ConfigFile, llvm::vfs::FileSystem *FS, return Text; } +// Resolves an explicit file: reference in a BasedOnStyle directive to a +// canonical, absolute path, substituting the value of the CLANG_FORMAT_DIR +// environment variable if the path starts with $(CLANG_FORMAT_DIR), or treating +// BasedOnFile as relative to the current ConfigFile otherwise. +static Expected> +resolveExplicitParentConfigFile(StringRef ConfigFile, StringRef BasedOnFile, +llvm::vfs::FileSystem *FS) { + constexpr const char *EnvVar = "CLANG_FORMAT_DIR"; + constexpr const char *EnvVarExpansion = "$(CLANG_FORMAT_DIR)"; + if (BasedOnFile.starts_with(EnvVarExpansion)) { +const char *ClangFormatDir = getenv(EnvVar); +if (ClangFormatDir == nullptr) { + return make_string_error(ConfigFile + ": 'BasedOnStyle: " + BasedOnFile + + "' uses " + EnvVarExpansion + ", but the " + + EnvVar + " environment variable is not defined"); +} else { + auto Status = FS->status(ClangFormatDir); + if (!Status || + Status->getType() != llvm::sys::fs::file_type::directory_file) { +return make_string_error( +StringRef("Environment variable ") + EnvVar + " = " + +ClangFormatDir + "does not refer to a valid, readable directory"); + } + + SmallString<128> FileName{ClangFormatDir}; + llvm::sys::path::append(FileName, + BasedOnFile.substr(strlen(EnvVarExpansion))); + ; + llvm::sys::path::remove_dots(FileName, true /*remove_dot_dot*/); + return FileName; +} + } else { +SmallString<128> FileName = llvm::sys::path::parent_path(ConfigFile); +llvm::sys::path::append(FileName, BasedOnFile); +llvm::sys::path::re