1 //===-- ConfigYAMLTests.cpp -----------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "Annotations.h" 10 #include "ConfigFragment.h" 11 #include "ConfigTesting.h" 12 #include "Protocol.h" 13 #include "llvm/ADT/StringRef.h" 14 #include "llvm/Support/SMLoc.h" 15 #include "llvm/Support/SourceMgr.h" 16 #include "llvm/Testing/Support/SupportHelpers.h" 17 #include "gmock/gmock.h" 18 #include "gtest/gtest.h" 19 20 namespace clang { 21 namespace clangd { 22 namespace config { 23 24 // PrintTo is a magic identifier of GTest 25 // NOLINTNEXTLINE (readability-identifier-naming) 26 template <typename T> void PrintTo(const Located<T> &V, std::ostream *OS) { 27 *OS << ::testing::PrintToString(*V); 28 } 29 30 namespace { 31 using ::testing::AllOf; 32 using ::testing::ElementsAre; 33 using ::testing::IsEmpty; 34 35 MATCHER_P(val, Value, "") { 36 if (*arg == Value) 37 return true; 38 *result_listener << "value is " << *arg; 39 return false; 40 } 41 42 MATCHER_P2(PairVal, Value1, Value2, "") { 43 if (*arg.first == Value1 && *arg.second == Value2) 44 return true; 45 *result_listener << "values are [" << *arg.first << ", " << *arg.second 46 << "]"; 47 return false; 48 } 49 50 TEST(ParseYAML, SyntacticForms) { 51 CapturedDiags Diags; 52 const char *YAML = R"yaml( 53 If: 54 PathMatch: 55 - 'abc' 56 CompileFlags: { Add: [foo, bar] } 57 --- 58 CompileFlags: 59 Add: | 60 b 61 az 62 --- 63 Index: 64 Background: Skip 65 --- 66 Diagnostics: 67 ClangTidy: 68 CheckOptions: 69 IgnoreMacros: true 70 example-check.ExampleOption: 0 71 UnusedIncludes: Strict 72 )yaml"; 73 auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback()); 74 EXPECT_THAT(Diags.Diagnostics, IsEmpty()); 75 EXPECT_THAT(Diags.Files, ElementsAre("config.yaml")); 76 ASSERT_EQ(Results.size(), 4u); 77 EXPECT_FALSE(Results[0].If.HasUnrecognizedCondition); 78 EXPECT_THAT(Results[0].If.PathMatch, ElementsAre(val("abc"))); 79 EXPECT_THAT(Results[0].CompileFlags.Add, ElementsAre(val("foo"), val("bar"))); 80 81 EXPECT_THAT(Results[1].CompileFlags.Add, ElementsAre(val("b\naz\n"))); 82 83 ASSERT_TRUE(Results[2].Index.Background); 84 EXPECT_EQ("Skip", **Results[2].Index.Background); 85 EXPECT_THAT(Results[3].Diagnostics.ClangTidy.CheckOptions, 86 ElementsAre(PairVal("IgnoreMacros", "true"), 87 PairVal("example-check.ExampleOption", "0"))); 88 EXPECT_TRUE(Results[3].Diagnostics.UnusedIncludes); 89 EXPECT_EQ("Strict", **Results[3].Diagnostics.UnusedIncludes); 90 } 91 92 TEST(ParseYAML, Locations) { 93 CapturedDiags Diags; 94 Annotations YAML(R"yaml( 95 If: 96 PathMatch: [['???bad***regex(((']] 97 )yaml"); 98 auto Results = 99 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 100 EXPECT_THAT(Diags.Diagnostics, IsEmpty()); 101 ASSERT_EQ(Results.size(), 1u); 102 ASSERT_NE(Results.front().Source.Manager, nullptr); 103 EXPECT_EQ(toRange(Results.front().If.PathMatch.front().Range, 104 *Results.front().Source.Manager), 105 YAML.range()); 106 } 107 108 TEST(ParseYAML, ConfigDiagnostics) { 109 CapturedDiags Diags; 110 Annotations YAML(R"yaml( 111 If: 112 $unknown[[UnknownCondition]]: "foo" 113 CompileFlags: 114 Add: 'first' 115 --- 116 CompileFlags: {$unexpected^ 117 )yaml"); 118 auto Results = 119 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 120 121 ASSERT_THAT( 122 Diags.Diagnostics, 123 ElementsAre(AllOf(diagMessage("Unknown If key 'UnknownCondition'"), 124 diagKind(llvm::SourceMgr::DK_Warning), 125 diagPos(YAML.range("unknown").start), 126 diagRange(YAML.range("unknown"))), 127 AllOf(diagMessage("Unexpected token. Expected Key, Flow " 128 "Entry, or Flow Mapping End."), 129 diagKind(llvm::SourceMgr::DK_Error), 130 diagPos(YAML.point("unexpected")), 131 diagRange(std::nullopt)))); 132 133 ASSERT_EQ(Results.size(), 1u); // invalid fragment discarded. 134 EXPECT_THAT(Results.front().CompileFlags.Add, ElementsAre(val("first"))); 135 EXPECT_TRUE(Results.front().If.HasUnrecognizedCondition); 136 } 137 138 TEST(ParseYAML, Invalid) { 139 CapturedDiags Diags; 140 const char *YAML = R"yaml( 141 If: 142 143 horrible 144 --- 145 - 1 146 )yaml"; 147 auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback()); 148 EXPECT_THAT(Diags.Diagnostics, 149 ElementsAre(diagMessage("If should be a dictionary"), 150 diagMessage("Config should be a dictionary"))); 151 ASSERT_THAT(Results, IsEmpty()); 152 } 153 154 TEST(ParseYAML, ExternalBlockNone) { 155 CapturedDiags Diags; 156 Annotations YAML(R"yaml( 157 Index: 158 External: None 159 )yaml"); 160 auto Results = 161 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 162 ASSERT_THAT(Diags.Diagnostics, IsEmpty()); 163 ASSERT_EQ(Results.size(), 1u); 164 ASSERT_TRUE(Results[0].Index.External); 165 EXPECT_FALSE((*Results[0].Index.External)->File.has_value()); 166 EXPECT_FALSE((*Results[0].Index.External)->MountPoint.has_value()); 167 EXPECT_FALSE((*Results[0].Index.External)->Server.has_value()); 168 EXPECT_THAT(*(*Results[0].Index.External)->IsNone, testing::Eq(true)); 169 } 170 171 TEST(ParseYAML, ExternalBlock) { 172 CapturedDiags Diags; 173 Annotations YAML(R"yaml( 174 Index: 175 External: 176 File: "foo" 177 Server: ^"bar" 178 MountPoint: "baz" 179 )yaml"); 180 auto Results = 181 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 182 ASSERT_EQ(Results.size(), 1u); 183 ASSERT_TRUE(Results[0].Index.External); 184 EXPECT_THAT(*(*Results[0].Index.External)->File, val("foo")); 185 EXPECT_THAT(*(*Results[0].Index.External)->MountPoint, val("baz")); 186 ASSERT_THAT(Diags.Diagnostics, IsEmpty()); 187 EXPECT_THAT(*(*Results[0].Index.External)->Server, val("bar")); 188 } 189 190 TEST(ParseYAML, AllScopes) { 191 CapturedDiags Diags; 192 Annotations YAML(R"yaml( 193 Completion: 194 AllScopes: True 195 )yaml"); 196 auto Results = 197 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 198 ASSERT_THAT(Diags.Diagnostics, IsEmpty()); 199 ASSERT_EQ(Results.size(), 1u); 200 EXPECT_THAT(Results[0].Completion.AllScopes, llvm::ValueIs(val(true))); 201 } 202 203 TEST(ParseYAML, AllScopesWarn) { 204 CapturedDiags Diags; 205 Annotations YAML(R"yaml( 206 Completion: 207 AllScopes: $diagrange[[Truex]] 208 )yaml"); 209 auto Results = 210 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 211 EXPECT_THAT(Diags.Diagnostics, 212 ElementsAre(AllOf(diagMessage("AllScopes should be a boolean"), 213 diagKind(llvm::SourceMgr::DK_Warning), 214 diagPos(YAML.range("diagrange").start), 215 diagRange(YAML.range("diagrange"))))); 216 ASSERT_EQ(Results.size(), 1u); 217 EXPECT_THAT(Results[0].Completion.AllScopes, testing::Eq(std::nullopt)); 218 } 219 220 TEST(ParseYAML, ShowAKA) { 221 CapturedDiags Diags; 222 Annotations YAML(R"yaml( 223 Hover: 224 ShowAKA: True 225 )yaml"); 226 auto Results = 227 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 228 ASSERT_THAT(Diags.Diagnostics, IsEmpty()); 229 ASSERT_EQ(Results.size(), 1u); 230 EXPECT_THAT(Results[0].Hover.ShowAKA, llvm::ValueIs(val(true))); 231 } 232 233 TEST(ParseYAML, InlayHints) { 234 CapturedDiags Diags; 235 Annotations YAML(R"yaml( 236 InlayHints: 237 Enabled: No 238 ParameterNames: Yes 239 )yaml"); 240 auto Results = 241 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 242 ASSERT_THAT(Diags.Diagnostics, IsEmpty()); 243 ASSERT_EQ(Results.size(), 1u); 244 EXPECT_THAT(Results[0].InlayHints.Enabled, llvm::ValueIs(val(false))); 245 EXPECT_THAT(Results[0].InlayHints.ParameterNames, llvm::ValueIs(val(true))); 246 EXPECT_EQ(Results[0].InlayHints.DeducedTypes, std::nullopt); 247 } 248 249 TEST(ParseYAML, SemanticTokens) { 250 CapturedDiags Diags; 251 Annotations YAML(R"yaml( 252 SemanticTokens: 253 DisabledKinds: [ Operator, InactiveCode] 254 DisabledModifiers: Readonly 255 )yaml"); 256 auto Results = 257 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 258 ASSERT_THAT(Diags.Diagnostics, IsEmpty()); 259 ASSERT_EQ(Results.size(), 1u); 260 EXPECT_THAT(Results[0].SemanticTokens.DisabledKinds, 261 ElementsAre(val("Operator"), val("InactiveCode"))); 262 EXPECT_THAT(Results[0].SemanticTokens.DisabledModifiers, 263 ElementsAre(val("Readonly"))); 264 } 265 266 TEST(ParseYAML, IncludesIgnoreHeader) { 267 CapturedDiags Diags; 268 Annotations YAML(R"yaml( 269 Diagnostics: 270 Includes: 271 IgnoreHeader: [foo, bar] 272 )yaml"); 273 auto Results = 274 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 275 ASSERT_THAT(Diags.Diagnostics, IsEmpty()); 276 ASSERT_EQ(Results.size(), 1u); 277 EXPECT_THAT(Results[0].Diagnostics.Includes.IgnoreHeader, 278 ElementsAre(val("foo"), val("bar"))); 279 } 280 281 TEST(ParseYAML, IncludesAnalyzeAngledIncludes) { 282 CapturedDiags Diags; 283 Annotations YAML(R"yaml( 284 Diagnostics: 285 Includes: 286 AnalyzeAngledIncludes: true 287 )yaml"); 288 auto Results = 289 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 290 ASSERT_THAT(Diags.Diagnostics, IsEmpty()); 291 ASSERT_EQ(Results.size(), 1u); 292 EXPECT_THAT(Results[0].Diagnostics.Includes.AnalyzeAngledIncludes, 293 llvm::ValueIs(val(true))); 294 } 295 296 TEST(ParseYAML, Style) { 297 CapturedDiags Diags; 298 Annotations YAML(R"yaml( 299 Style: 300 FullyQualifiedNamespaces: [foo, bar] 301 AngledHeaders: ["foo", "bar"] 302 QuotedHeaders: ["baz", "baar"])yaml"); 303 auto Results = 304 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); 305 ASSERT_THAT(Diags.Diagnostics, IsEmpty()); 306 ASSERT_EQ(Results.size(), 1u); 307 EXPECT_THAT(Results[0].Style.FullyQualifiedNamespaces, 308 ElementsAre(val("foo"), val("bar"))); 309 EXPECT_THAT(Results[0].Style.AngledHeaders, 310 ElementsAre(val("foo"), val("bar"))); 311 EXPECT_THAT(Results[0].Style.QuotedHeaders, 312 ElementsAre(val("baz"), val("baar"))); 313 } 314 } // namespace 315 } // namespace config 316 } // namespace clangd 317 } // namespace clang 318