sammccall updated this revision to Diff 154391.
sammccall added a comment.
Herald added a subscriber: mgorny.

Name debug->verbose


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D49008

Files:
  clang-tidy/cert/CERTTidyModule.cpp
  clang-tidy/cert/CMakeLists.txt
  clang-tidy/cert/ProperlySeededRandomGeneratorCheck.cpp
  clang-tidy/cert/ProperlySeededRandomGeneratorCheck.h
  clang-tidy/misc/UnusedParametersCheck.cpp
  clangd/ClangdLSPServer.cpp
  clangd/ClangdServer.cpp
  clangd/ClangdUnit.cpp
  clangd/CodeComplete.cpp
  clangd/CodeComplete.h
  clangd/Compiler.cpp
  clangd/Diagnostics.cpp
  clangd/FileDistance.cpp
  clangd/FindSymbols.cpp
  clangd/GlobalCompilationDatabase.cpp
  clangd/JSONRPCDispatcher.cpp
  clangd/JSONRPCDispatcher.h
  clangd/Logger.cpp
  clangd/Logger.h
  clangd/Protocol.cpp
  clangd/ProtocolHandlers.cpp
  clangd/Quality.cpp
  clangd/TUScheduler.cpp
  clangd/XRefs.cpp
  clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp
  clangd/index/FileIndex.cpp
  clangd/index/Index.cpp
  clangd/index/Index.h
  clangd/index/Merge.cpp
  clangd/index/SymbolCollector.cpp
  clangd/index/SymbolCollector.h
  clangd/tool/ClangdMain.cpp
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/cert-msc32-c.rst
  docs/clang-tidy/checks/cert-msc51-cpp.rst
  docs/clang-tidy/checks/list.rst
  test/clang-tidy/cert-msc32-c.c
  test/clang-tidy/cert-msc51-cpp.cpp
  test/clang-tidy/misc-unused-parameters.cpp
  unittests/clangd/CodeCompleteTests.cpp
  unittests/clangd/IndexTests.cpp
  unittests/clangd/QualityTests.cpp
  unittests/clangd/SymbolCollectorTests.cpp
  unittests/clangd/TestTU.cpp
  unittests/clangd/TestTU.h

Index: unittests/clangd/TestTU.h
===================================================================
--- unittests/clangd/TestTU.h
+++ unittests/clangd/TestTU.h
@@ -56,6 +56,9 @@
 const Symbol &findSymbol(const SymbolSlab &, llvm::StringRef QName);
 // Look up an AST symbol by qualified name, which must be unique and top-level.
 const NamedDecl &findDecl(ParsedAST &AST, llvm::StringRef QName);
+// Look up a main-file AST symbol that satisfies \p Filter.
+const NamedDecl &findAnyDecl(ParsedAST &AST,
+                             std::function<bool(const NamedDecl &)> Filter);
 // Look up a main-file AST symbol by unqualified name, which must be unique.
 const NamedDecl &findAnyDecl(ParsedAST &AST, llvm::StringRef Name);
 
Index: unittests/clangd/TestTU.cpp
===================================================================
--- unittests/clangd/TestTU.cpp
+++ unittests/clangd/TestTU.cpp
@@ -93,26 +93,35 @@
   return LookupDecl(*Scope, Components.back());
 }
 
-const NamedDecl &findAnyDecl(ParsedAST &AST, llvm::StringRef Name) {
+const NamedDecl &findAnyDecl(ParsedAST &AST,
+                             std::function<bool(const NamedDecl &)> Callback) {
   struct Visitor : RecursiveASTVisitor<Visitor> {
-    llvm::StringRef Name;
+    decltype(Callback) CB;
     llvm::SmallVector<const NamedDecl *, 1> Decls;
     bool VisitNamedDecl(const NamedDecl *ND) {
-      if (auto *ID = ND->getIdentifier())
-        if (ID->getName() == Name)
-          Decls.push_back(ND);
+      if (CB(*ND))
+        Decls.push_back(ND);
       return true;
     }
   } Visitor;
-  Visitor.Name = Name;
+  Visitor.CB = Callback;
   for (Decl *D : AST.getLocalTopLevelDecls())
     Visitor.TraverseDecl(D);
   if (Visitor.Decls.size() != 1) {
-    ADD_FAILURE() << Visitor.Decls.size() << " symbols named " << Name;
+    ADD_FAILURE() << Visitor.Decls.size() << " symbols matched.";
     assert(Visitor.Decls.size() == 1);
   }
   return *Visitor.Decls.front();
 }
 
+const NamedDecl &findAnyDecl(ParsedAST &AST, llvm::StringRef Name) {
+  return findAnyDecl(AST, [Name](const NamedDecl &ND) {
+    if (auto *ID = ND.getIdentifier())
+      if (ID->getName() == Name)
+        return true;
+    return false;
+  });
+}
+
 } // namespace clangd
 } // namespace clang
Index: unittests/clangd/SymbolCollectorTests.cpp
===================================================================
--- unittests/clangd/SymbolCollectorTests.cpp
+++ unittests/clangd/SymbolCollectorTests.cpp
@@ -985,6 +985,13 @@
                                             AllOf(QName("Y"), Refs(1))));
 }
 
+TEST_F(SymbolCollectorTest, Origin) {
+  CollectorOpts.Origin = SymbolOrigin::Static;
+  runSymbolCollector("class Foo {};", /*Main=*/"");
+  EXPECT_THAT(Symbols, UnorderedElementsAre(
+                           Field(&Symbol::Origin, SymbolOrigin::Static)));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: unittests/clangd/QualityTests.cpp
===================================================================
--- unittests/clangd/QualityTests.cpp
+++ unittests/clangd/QualityTests.cpp
@@ -21,6 +21,9 @@
 #include "Quality.h"
 #include "TestFS.h"
 #include "TestTU.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "llvm/Support/Casting.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -199,6 +202,31 @@
   EXPECT_LT(sortText(0, "a"), sortText(0, "z"));
 }
 
+TEST(QualityTests, NoBoostForClassConstructor) {
+  auto Header = TestTU::withHeaderCode(R"cpp(
+    class Foo {
+    public:
+      Foo(int);
+    };
+  )cpp");
+  auto Symbols = Header.headerSymbols();
+  auto AST = Header.build();
+
+  const NamedDecl *Foo = &findDecl(AST, "Foo");
+  SymbolRelevanceSignals Cls;
+  Cls.merge(CodeCompletionResult(Foo, /*Priority=*/0));
+
+  const NamedDecl *CtorDecl = &findAnyDecl(AST, [](const NamedDecl &ND) {
+    return (ND.getQualifiedNameAsString() == "Foo::Foo") &&
+           llvm::isa<CXXConstructorDecl>(&ND);
+  });
+  SymbolRelevanceSignals Ctor;
+  Ctor.merge(CodeCompletionResult(CtorDecl, /*Priority=*/0));
+
+  EXPECT_EQ(Cls.Scope, SymbolRelevanceSignals::GlobalScope);
+  EXPECT_EQ(Ctor.Scope, SymbolRelevanceSignals::GlobalScope);
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: unittests/clangd/IndexTests.cpp
===================================================================
--- unittests/clangd/IndexTests.cpp
+++ unittests/clangd/IndexTests.cpp
@@ -282,6 +282,8 @@
   DetR.Documentation = "--doc--";
   L.Detail = &DetL;
   R.Detail = &DetR;
+  L.Origin = SymbolOrigin::Dynamic;
+  R.Origin = SymbolOrigin::Static;
 
   Symbol::Details Scratch;
   Symbol M = mergeSymbol(L, R, &Scratch);
@@ -293,6 +295,8 @@
   ASSERT_TRUE(M.Detail);
   EXPECT_EQ(M.Detail->ReturnType, "DetL");
   EXPECT_EQ(M.Detail->Documentation, "--doc--");
+  EXPECT_EQ(M.Origin,
+            SymbolOrigin::Dynamic | SymbolOrigin::Static | SymbolOrigin::Merge);
 }
 
 TEST(MergeTest, PreferSymbolWithDefn) {
Index: unittests/clangd/CodeCompleteTests.cpp
===================================================================
--- unittests/clangd/CodeCompleteTests.cpp
+++ unittests/clangd/CodeCompleteTests.cpp
@@ -59,6 +59,7 @@
 }
 MATCHER(InsertInclude, "") { return bool(arg.HeaderInsertion); }
 MATCHER_P(SnippetSuffix, Text, "") { return arg.SnippetSuffix == Text; }
+MATCHER_P(Origin, OriginSet, "") { return arg.Origin == OriginSet; }
 
 // Shorthand for Contains(Named(Name)).
 Matcher<const std::vector<CodeCompletion> &> Has(std::string Name) {
@@ -137,6 +138,7 @@
   Sym.ID = SymbolID(USR);
   Sym.SymInfo.Kind = Kind;
   Sym.IsIndexedForCodeCompletion = true;
+  Sym.Origin = SymbolOrigin::Static;
   return Sym;
 }
 Symbol func(StringRef Name) { // Assumes the function has no args.
@@ -511,9 +513,12 @@
       )cpp",
       {func("ns::both"), cls("ns::Index")});
   // We get results from both index and sema, with no duplicates.
-  EXPECT_THAT(
-      Results.Completions,
-      UnorderedElementsAre(Named("local"), Named("Index"), Named("both")));
+  EXPECT_THAT(Results.Completions,
+              UnorderedElementsAre(
+                  AllOf(Named("local"), Origin(SymbolOrigin::AST)),
+                  AllOf(Named("Index"), Origin(SymbolOrigin::Static)),
+                  AllOf(Named("both"),
+                        Origin(SymbolOrigin::AST | SymbolOrigin::Static))));
 }
 
 TEST(CompletionTest, SemaIndexMergeWithLimit) {
@@ -1252,6 +1257,8 @@
   C.Header = "\"foo.h\"";
   C.Kind = CompletionItemKind::Method;
   C.Score.Total = 1.0;
+  C.Origin =
+      static_cast<SymbolOrigin>(SymbolOrigin::AST | SymbolOrigin::Static);
 
   CodeCompleteOptions Opts;
   Opts.IncludeIndicator.Insert = "^";
@@ -1278,6 +1285,10 @@
   EXPECT_EQ(R.label, "^Foo::x(bool) const");
   EXPECT_THAT(R.additionalTextEdits, Not(IsEmpty()));
 
+  Opts.ShowOrigins = true;
+  R = C.render(Opts);
+  EXPECT_EQ(R.label, "^[AS]Foo::x(bool) const");
+
   C.BundleSize = 2;
   R = C.render(Opts);
   EXPECT_EQ(R.detail, "[2 overloads]\n\"foo.h\"");
Index: test/clang-tidy/misc-unused-parameters.cpp
===================================================================
--- test/clang-tidy/misc-unused-parameters.cpp
+++ test/clang-tidy/misc-unused-parameters.cpp
@@ -222,6 +222,21 @@
   return Function<void(int, int)>();
 }
 
+namespace PR38055 {
+namespace {
+struct a {
+  void b(int c) {;}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: parameter 'c' is unused
+// CHECK-FIXES: {{^}}  void b() {;}{{$}}
+};
+template <class>
+class d {
+  a e;
+  void f() { e.b(); }
+};
+}  // namespace
+}  // namespace PR38055
+
 namespace strict_mode_off {
 // Do not warn on empty function bodies.
 void f1(int foo1) {}
Index: test/clang-tidy/cert-msc51-cpp.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/cert-msc51-cpp.cpp
@@ -0,0 +1,210 @@
+// RUN: %check_clang_tidy %s cert-msc51-cpp %t -- -config="{CheckOptions: [{key: cert-msc51-cpp.DisallowedSeedTypes, value: 'some_type,time_t'}]}" -- -std=c++11
+
+namespace std {
+
+void srand(int seed);
+
+template <class UIntType, UIntType a, UIntType c, UIntType m>
+struct linear_congruential_engine {
+  linear_congruential_engine(int _ = 0);
+  void seed(int _ = 0);
+};
+using default_random_engine = linear_congruential_engine<unsigned int, 1, 2, 3>;
+
+using size_t = int;
+template <class UIntType, size_t w, size_t n, size_t m, size_t r,
+          UIntType a, size_t u, UIntType d, size_t s,
+          UIntType b, size_t t,
+          UIntType c, size_t l, UIntType f>
+struct mersenne_twister_engine {
+  mersenne_twister_engine(int _ = 0);
+  void seed(int _ = 0);
+};
+using mt19937 = mersenne_twister_engine<unsigned int, 32, 624, 397, 21, 0x9908b0df, 11, 0xffffffff, 7, 0x9d2c5680, 15, 0xefc60000, 18, 1812433253>;
+
+template <class UIntType, size_t w, size_t s, size_t r>
+struct subtract_with_carry_engine {
+  subtract_with_carry_engine(int _ = 0);
+  void seed(int _ = 0);
+};
+using ranlux24_base = subtract_with_carry_engine<unsigned int, 24, 10, 24>;
+
+template <class Engine, size_t p, size_t r>
+struct discard_block_engine {
+  discard_block_engine();
+  discard_block_engine(int _);
+  void seed();
+  void seed(int _);
+};
+using ranlux24 = discard_block_engine<ranlux24_base, 223, 23>;
+
+template <class Engine, size_t w, class UIntType>
+struct independent_bits_engine {
+  independent_bits_engine();
+  independent_bits_engine(int _);
+  void seed();
+  void seed(int _);
+};
+using independent_bits = independent_bits_engine<ranlux24_base, 223, int>;
+
+template <class Engine, size_t k>
+struct shuffle_order_engine {
+  shuffle_order_engine();
+  shuffle_order_engine(int _);
+  void seed();
+  void seed(int _);
+};
+using shuffle_order = shuffle_order_engine<ranlux24_base, 223>;
+
+struct random_device {
+  random_device();
+  int operator()();
+};
+} // namespace std
+
+using time_t = unsigned int;
+time_t time(time_t *t);
+
+void f() {
+  const int seed = 2;
+  time_t t;
+
+  std::srand(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::srand(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::srand(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+
+  // One instantiation for every engine
+  std::default_random_engine engine1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  std::default_random_engine engine2(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::default_random_engine engine3(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::default_random_engine engine4(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine1.seed();
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  engine1.seed(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine1.seed(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine1.seed(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+
+  std::mt19937 engine5;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  std::mt19937 engine6(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::mt19937 engine7(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::mt19937 engine8(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine5.seed();
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  engine5.seed(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine5.seed(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine5.seed(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+
+  std::ranlux24_base engine9;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  std::ranlux24_base engine10(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::ranlux24_base engine11(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::ranlux24_base engine12(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine9.seed();
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  engine9.seed(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine9.seed(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine9.seed(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+
+  std::ranlux24 engine13;
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  std::ranlux24 engine14(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::ranlux24 engine15(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::ranlux24 engine16(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine13.seed();
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  engine13.seed(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine13.seed(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine13.seed(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+
+  std::independent_bits engine17;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  std::independent_bits engine18(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::independent_bits engine19(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::independent_bits engine20(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine17.seed();
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  engine17.seed(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine17.seed(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine17.seed(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+
+  std::shuffle_order engine21;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  std::shuffle_order engine22(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::shuffle_order engine23(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  std::shuffle_order engine24(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine21.seed();
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a default argument will generate a predictable sequence of values [cert-msc51-cpp]
+  engine21.seed(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine21.seed(seed);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc51-cpp]
+  engine21.seed(time(&t));
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc51-cpp]
+}
+
+struct A {
+  A(int _ = 0);
+  void seed(int _ = 0);
+};
+
+void g() {
+  int n = 1;
+  std::default_random_engine engine1(n);
+  std::mt19937 engine2(n);
+  std::ranlux24_base engine3(n);
+  std::ranlux24 engine4(n);
+  std::independent_bits engine5(n);
+  std::shuffle_order engine6(n);
+
+  std::random_device dev;
+  std::default_random_engine engine7(dev());
+  std::mt19937 engine8(dev());
+  std::ranlux24_base engine9(dev());
+  std::ranlux24 engine10(dev());
+  std::independent_bits engine11(dev());
+  std::shuffle_order engine12(dev());
+
+  A a1;
+  A a2(1);
+  a1.seed();
+  a1.seed(1);
+  a1.seed(n);
+}
Index: test/clang-tidy/cert-msc32-c.c
===================================================================
--- /dev/null
+++ test/clang-tidy/cert-msc32-c.c
@@ -0,0 +1,27 @@
+// RUN: %check_clang_tidy %s cert-msc32-c %t -- -config="{CheckOptions: [{key: cert-msc32-c.DisallowedSeedTypes, value: 'some_type,time_t'}]}" -- -std=c99
+
+void srand(int seed);
+typedef int time_t;
+time_t time(time_t *t);
+
+void f() {
+  srand(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc32-c]
+
+  const int a = 1;
+  srand(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: random number generator seeded with a constant value will generate a predictable sequence of values [cert-msc32-c]
+
+  time_t t;
+  srand(time(&t)); // Disallowed seed type
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: random number generator seeded with a disallowed source of seed value will generate a predictable sequence of values [cert-msc32-c]
+}
+
+void g() {
+  typedef int user_t;
+  user_t a = 1;
+  srand(a);
+
+  int b = 1;
+  srand(b); // Can not evaluate as int
+}
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -73,7 +73,9 @@
    cert-fio38-c (redirects to misc-non-copyable-objects) <cert-fio38-c>
    cert-flp30-c
    cert-msc30-c (redirects to cert-msc50-cpp) <cert-msc30-c>
+   cert-msc32-c (redirects to cert-msc51-cpp) <cert-msc32-c>
    cert-msc50-cpp
+   cert-msc51-cpp
    cert-oop11-cpp (redirects to performance-move-constructor-init) <cert-oop11-cpp>
    cppcoreguidelines-avoid-goto
    cppcoreguidelines-c-copy-assignment-signature (redirects to misc-unconventional-assign-operator) <cppcoreguidelines-c-copy-assignment-signature>
Index: docs/clang-tidy/checks/cert-msc51-cpp.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/cert-msc51-cpp.rst
@@ -0,0 +1,40 @@
+.. title:: clang-tidy - cert-msc51-cpp
+
+cert-msc51-cpp
+==============
+
+This check flags all pseudo-random number engines, engine adaptor
+instantiations and ``srand()`` when initialized or seeded with default argument,
+constant expression or any user-configurable type. Pseudo-random number
+engines seeded with a predictable value may cause vulnerabilities e.g. in
+security protocols.
+This is a CERT security rule, see
+`MSC51-CPP. Ensure your random number generator is properly seeded
+<https://wiki.sei.cmu.edu/confluence/display/cplusplus/MSC51-CPP.+Ensure+your+random+number+generator+is+properly+seeded>`_ and
+`MSC32-C. Properly seed pseudorandom number generators
+<https://wiki.sei.cmu.edu/confluence/display/c/MSC32-C.+Properly+seed+pseudorandom+number+generators>`_.
+
+Examples:
+
+.. code-block:: c++
+
+  void foo() {
+    std::mt19937 engine1; // Diagnose, always generate the same sequence
+    std::mt19937 engine2(1); // Diagnose
+    engine1.seed(); // Diagnose
+    engine2.seed(1); // Diagnose
+    
+    std::time_t t;
+    engine1.seed(std::time(&t)); // Diagnose, system time might be controlled by user
+
+    int x = atoi(argv[1]);
+    std::mt19937 engine3(x);  // Will not warn
+  }
+
+Options
+-------
+
+.. option:: DisallowedSeedTypes
+
+   A comma-separated list of the type names which are disallowed.
+   Default values are ``time_t``, ``std::time_t``.
Index: docs/clang-tidy/checks/cert-msc32-c.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/cert-msc32-c.rst
@@ -0,0 +1,9 @@
+.. title:: clang-tidy - cert-msc32-c
+.. meta::
+   :http-equiv=refresh: 5;URL=cert-msc51-cpp.html
+
+cert-msc32-c
+============
+
+The cert-msc32-c check is an alias, please see
+`cert-msc51-cpp <cert-msc51-cpp.html>`_ for more information.
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -253,6 +253,17 @@
 
 - The 'google-runtime-member-string-references' check was removed.
 
+- New `cert-msc51-cpp
+  <http://clang.llvm.org/extra/clang-tidy/checks/cert-properly-seeded-random-generator.html>`_ check
+
+  Detects inappropriate seeding of C++ random generators and C ``srand()`` function.
+  
+- New `cert-msc32-c
+  <http://clang.llvm.org/extra/clang-tidy/checks/cert-properly-seeded-random-generator.html>`_ check
+
+  Detects inappropriate seeding of ``srand()`` function.
+
+
 Improvements to include-fixer
 -----------------------------
 
Index: clangd/tool/ClangdMain.cpp
===================================================================
--- clangd/tool/ClangdMain.cpp
+++ clangd/tool/ClangdMain.cpp
@@ -98,6 +98,14 @@
     PrettyPrint("pretty", llvm::cl::desc("Pretty-print JSON output"),
                 llvm::cl::init(false));
 
+static llvm::cl::opt<Logger::Level> LogLevel(
+    "log", llvm::cl::desc("Verbosity of log messages written to stderr"),
+    llvm::cl::values(clEnumValN(Logger::Error, "error", "Error messages only"),
+                     clEnumValN(Logger::Info, "info",
+                                "High level execution tracing"),
+                     clEnumValN(Logger::Debug, "verbose", "Low level details")),
+    llvm::cl::init(Logger::Info));
+
 static llvm::cl::opt<bool> Test(
     "lit-test",
     llvm::cl::desc(
@@ -143,6 +151,12 @@
                    "Clang uses an index built from symbols in opened files"),
     llvm::cl::init(true));
 
+static llvm::cl::opt<bool>
+    ShowOrigins("debug-origin",
+                llvm::cl::desc("Show origins of completion items"),
+                llvm::cl::init(clangd::CodeCompleteOptions().ShowOrigins),
+                llvm::cl::Hidden);
+
 static llvm::cl::opt<Path> YamlSymbolFile(
     "yaml-symbol-file",
     llvm::cl::desc(
@@ -217,7 +231,7 @@
   if (Tracer)
     TracingSession.emplace(*Tracer);
 
-  JSONOutput Out(llvm::outs(), llvm::errs(),
+  JSONOutput Out(llvm::outs(), llvm::errs(), LogLevel,
                  InputMirrorStream ? InputMirrorStream.getPointer() : nullptr,
                  PrettyPrint);
 
@@ -261,6 +275,7 @@
   CCOpts.IncludeIneligibleResults = IncludeIneligibleResults;
   CCOpts.Limit = LimitResults;
   CCOpts.BundleOverloads = CompletionStyle != Detailed;
+  CCOpts.ShowOrigins = ShowOrigins;
 
   // Initialize and run ClangdLSPServer.
   ClangdLSPServer LSPServer(Out, CCOpts, CompileCommandsDirPath, Opts);
Index: clangd/index/SymbolCollector.h
===================================================================
--- clangd/index/SymbolCollector.h
+++ clangd/index/SymbolCollector.h
@@ -52,6 +52,8 @@
     const CanonicalIncludes *Includes = nullptr;
     // Populate the Symbol.References field.
     bool CountReferences = false;
+    // Every symbol collected will be stamped with this origin.
+    SymbolOrigin Origin = SymbolOrigin::Unknown;
   };
 
   SymbolCollector(Options Opts);
Index: clangd/index/SymbolCollector.cpp
===================================================================
--- clangd/index/SymbolCollector.cpp
+++ clangd/index/SymbolCollector.cpp
@@ -52,7 +52,7 @@
   if (std::error_code EC =
           SM.getFileManager().getVirtualFileSystem()->makeAbsolute(
               AbsolutePath))
-    log("Warning: could not make absolute file: " + EC.message());
+    log("Warning: could not make absolute file: {0}", EC.message());
   if (llvm::sys::path::is_absolute(AbsolutePath)) {
     // Handle the symbolic link path case where the current working directory
     // (getCurrentWorkingDirectory) is a symlink./ We always want to the real
@@ -86,8 +86,7 @@
       return U->toString();
     ErrMsg += llvm::toString(U.takeError()) + "\n";
   }
-  log(llvm::Twine("Failed to create an URI for file ") + AbsolutePath + ": " +
-      ErrMsg);
+  log("Failed to create an URI for file {0}: {1}", AbsolutePath, ErrMsg);
   return llvm::None;
 }
 
@@ -411,6 +410,7 @@
   Detail.IncludeHeader = Include;
   S.Detail = &Detail;
 
+  S.Origin = Opts.Origin;
   Symbols.insert(S);
   return Symbols.find(S.ID);
 }
Index: clangd/index/Merge.cpp
===================================================================
--- clangd/index/Merge.cpp
+++ clangd/index/Merge.cpp
@@ -115,6 +115,9 @@
     } else
       S.Detail = O.Detail;
   }
+
+  S.Origin =
+      static_cast<SymbolOrigin>(S.Origin | O.Origin | SymbolOrigin::Merge);
   return S;
 }
 
Index: clangd/index/Index.h
===================================================================
--- clangd/index/Index.h
+++ clangd/index/Index.h
@@ -117,6 +117,19 @@
 namespace clang {
 namespace clangd {
 
+// Describes the source of information about a symbol.
+// Mainly useful for debugging, e.g. understanding code completion reuslts.
+// This is a bitfield as information can be combined from several sources.
+enum SymbolOrigin : uint8_t {
+  Unknown = 0,
+  AST = 1 << 0,     // Directly from the AST (indexes should not set this).
+  Dynamic = 1 << 1, // From the dynamic index of opened files.
+  Static = 1 << 2,  // From the static, externally-built index.
+  Merge = 1 << 3,   // A non-trivial index merge was performed.
+  // Remaining bits reserved for index implementations.
+};
+raw_ostream &operator<<(raw_ostream &, SymbolOrigin);
+
 // The class presents a C++ symbol, e.g. class, function.
 //
 // WARNING: Symbols do not own much of their underlying data - typically strings
@@ -157,6 +170,8 @@
   /// Whether or not this symbol is meant to be used for the code completion.
   /// See also isIndexedForCodeCompletion().
   bool IsIndexedForCodeCompletion = false;
+  /// Where this symbol came from. Usually an index provides a constant value.
+  SymbolOrigin Origin = Unknown;
   /// A brief description of the symbol that can be appended in the completion
   /// candidate list. For example, "(X x, Y y) const" is a function signature.
   llvm::StringRef Signature;
Index: clangd/index/Index.cpp
===================================================================
--- clangd/index/Index.cpp
+++ clangd/index/Index.cpp
@@ -44,6 +44,16 @@
   std::copy(HexString.begin(), HexString.end(), ID.HashValue.begin());
 }
 
+raw_ostream &operator<<(raw_ostream &OS, SymbolOrigin O) {
+  if (O == SymbolOrigin::Unknown)
+    return OS << "unknown";
+  constexpr static char Sigils[] = "ADSM4567";
+  for (unsigned I = 0; I < sizeof(Sigils); ++I)
+    if (O & static_cast<SymbolOrigin>(1 << I))
+      OS << Sigils[I];
+  return OS;
+}
+
 raw_ostream &operator<<(raw_ostream &OS, const Symbol &S) {
   return OS << S.Scope << S.Name;
 }
Index: clangd/index/FileIndex.cpp
===================================================================
--- clangd/index/FileIndex.cpp
+++ clangd/index/FileIndex.cpp
@@ -27,6 +27,7 @@
   CollectorOpts.CountReferences = false;
   if (!URISchemes.empty())
     CollectorOpts.URISchemes = URISchemes;
+  CollectorOpts.Origin = SymbolOrigin::Dynamic;
 
   SymbolCollector Collector(std::move(CollectorOpts));
   Collector.setPreprocessor(PP);
Index: clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp
===================================================================
--- clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp
+++ clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp
@@ -112,6 +112,7 @@
     CollectorOpts.FallbackDir = AssumedHeaderDir;
     CollectorOpts.CollectIncludePath = true;
     CollectorOpts.CountReferences = true;
+    CollectorOpts.Origin = SymbolOrigin::Static;
     auto Includes = llvm::make_unique<CanonicalIncludes>();
     addSystemHeadersMapping(Includes.get());
     CollectorOpts.Includes = Includes.get();
Index: clangd/XRefs.cpp
===================================================================
--- clangd/XRefs.cpp
+++ clangd/XRefs.cpp
@@ -46,12 +46,12 @@
     return llvm::None;
   auto Uri = URI::parse(Loc.FileURI);
   if (!Uri) {
-    log("Could not parse URI: " + Loc.FileURI);
+    log("Could not parse URI: {0}", Loc.FileURI);
     return llvm::None;
   }
   auto Path = URI::resolve(*Uri, HintPath);
   if (!Path) {
-    log("Could not resolve URI: " + Loc.FileURI);
+    log("Could not resolve URI: {0}", Loc.FileURI);
     return llvm::None;
   }
   Location LSPLoc;
@@ -182,7 +182,7 @@
     FilePath = F->getName();
   if (!llvm::sys::path::is_absolute(FilePath)) {
     if (!SourceMgr.getFileManager().makeAbsolutePath(FilePath)) {
-      log("Could not turn relative path to absolute: " + FilePath);
+      log("Could not turn relative path to absolute: {0}", FilePath);
       return llvm::None;
     }
   }
Index: clangd/TUScheduler.cpp
===================================================================
--- clangd/TUScheduler.cpp
+++ clangd/TUScheduler.cpp
@@ -321,14 +321,14 @@
     // Remove the old AST if it's still in cache.
     IdleASTs.take(this);
 
-    log("Updating file " + FileName + " with command [" +
-        Inputs.CompileCommand.Directory + "] " +
+    log("Updating file {0} with command [{1}] {2}", FileName,
+        Inputs.CompileCommand.Directory,
         llvm::join(Inputs.CompileCommand.CommandLine, " "));
     // Rebuild the preamble and the AST.
     std::unique_ptr<CompilerInvocation> Invocation =
         buildCompilerInvocation(Inputs);
     if (!Invocation) {
-      log("Could not build CompilerInvocation for file " + FileName);
+      elog("Could not build CompilerInvocation for file {0}", FileName);
       return;
     }
 
@@ -611,8 +611,8 @@
 void TUScheduler::remove(PathRef File) {
   bool Removed = Files.erase(File);
   if (!Removed)
-    log("Trying to remove file from TUScheduler that is not tracked. File:" +
-        File);
+    elog("Trying to remove file from TUScheduler that is not tracked: {0}",
+         File);
 }
 
 void TUScheduler::runWithAST(
Index: clangd/Quality.cpp
===================================================================
--- clangd/Quality.cpp
+++ clangd/Quality.cpp
@@ -11,10 +11,12 @@
 #include "URI.h"
 #include "index/Index.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclVisitor.h"
 #include "clang/Basic/CharInfo.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
@@ -195,6 +197,9 @@
   if (auto *R = dyn_cast_or_null<RecordDecl>(D))
     if (R->isInjectedClassName())
       DC = DC->getParent();
+  // Class constructor should have the same scope as the class.
+  if (const auto *Ctor = llvm::dyn_cast<CXXConstructorDecl>(D))
+    DC = DC->getParent();
   bool InClass = false;
   for (; !DC->isFileContext(); DC = DC->getParent()) {
     if (DC->isFunctionOrMethod())
Index: clangd/ProtocolHandlers.cpp
===================================================================
--- clangd/ProtocolHandlers.cpp
+++ clangd/ProtocolHandlers.cpp
@@ -32,7 +32,7 @@
       if (fromJSON(RawParams, P)) {
         (Callbacks->*Handler)(P);
       } else {
-        log("Failed to decode " + Method + " request.");
+        elog("Failed to decode {0} request.", Method);
       }
     });
   }
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -33,16 +33,17 @@
   if (auto S = E.asString()) {
     auto U = URI::parse(*S);
     if (!U) {
-      log("Failed to parse URI " + *S + ": " + llvm::toString(U.takeError()));
+      elog("Failed to parse URI {0}: {1}", *S, U.takeError());
       return false;
     }
     if (U->scheme() != "file" && U->scheme() != "test") {
-      log("Clangd only supports 'file' URI scheme for workspace files: " + *S);
+      elog("Clangd only supports 'file' URI scheme for workspace files: {0}",
+           *S);
       return false;
     }
     auto Path = URI::resolve(*U);
     if (!Path) {
-      log(llvm::toString(Path.takeError()));
+      log("{0}", Path.takeError());
       return false;
     }
     R = URIForFile(*Path);
Index: clangd/Logger.h
===================================================================
--- clangd/Logger.h
+++ clangd/Logger.h
@@ -11,24 +11,56 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_LOGGER_H
 
 #include "llvm/ADT/Twine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FormatVariadic.h"
 
 namespace clang {
 namespace clangd {
 
-/// Main logging function.
-/// Logs messages to a global logger, which can be set up by LoggingSesssion.
-/// If no logger is registered, writes to llvm::errs().
-void log(const llvm::Twine &Message);
-
 /// Interface to allow custom logging in clangd.
 class Logger {
 public:
   virtual ~Logger() = default;
 
+  enum Level { Debug, Verbose, Info, Error };
+  static char indicator(Level L) { return "DVIE"[L]; }
+
   /// Implementations of this method must be thread-safe.
-  virtual void log(const llvm::Twine &Message) = 0;
+  virtual void log(Level, const llvm::formatv_object_base &Message) = 0;
 };
 
+namespace detail {
+const char *debugType(const char *Filename);
+void log(Logger::Level, const llvm::formatv_object_base &);
+} // namespace detail
+
+// Clangd logging functions write to a global logger set by LoggingSession.
+// If no logger is registered, writes to llvm::errs().
+// All accept llvm::formatv()-style arguments, e.g. log("Text={0}", Text).
+
+// elog() is used for "loud" errors and warnings.
+// This level is often visible to users.
+template <typename... Ts> void elog(const char *Fmt, Ts &&... Vals) {
+  detail::log(Logger::Error, llvm::formatv(Fmt, std::forward<Ts>(Vals)...));
+}
+// log() is used for information important to understanding a clangd session.
+// e.g. the names of LSP messages sent are logged at this level.
+// This level could be enabled in production builds to allow later inspection.
+template <typename... Ts> void log(const char *Fmt, Ts &&... Vals) {
+  detail::log(Logger::Info, llvm::formatv(Fmt, std::forward<Ts>(Vals)...));
+}
+// vlog() is used for details often needed for debugging clangd sessions.
+// This level would typically be enabled for clangd developers.
+template <typename... Ts> void vlog(const char *Fmt, Ts &&... Vals) {
+  detail::log(Logger::Verbose, llvm::formatv(Fmt, std::forward<Ts>(Vals)...));
+}
+// dlog only logs if --debug was passed, or --debug_only=Basename.
+// This level would be enabled in a targeted way when debugging.
+#define dlog(...)                                                              \
+  DEBUG_WITH_TYPE(                                                             \
+      ::clang::clangd::detail::debugType(__FILE__),                            \
+      ::clang::clangd::detail::log(Logger::Debug, llvm::formatv(__VA_ARGS__)))
+
 /// Only one LoggingSession can be active at a time.
 class LoggingSession {
 public:
Index: clangd/Logger.cpp
===================================================================
--- clangd/Logger.cpp
+++ clangd/Logger.cpp
@@ -25,15 +25,24 @@
 
 LoggingSession::~LoggingSession() { L = nullptr; }
 
-void log(const llvm::Twine &Message) {
+void detail::log(Logger::Level Level,
+                 const llvm::formatv_object_base &Message) {
   if (L)
-    L->log(Message);
+    L->log(Level, Message);
   else {
     static std::mutex Mu;
     std::lock_guard<std::mutex> Guard(Mu);
     llvm::errs() << Message << "\n";
   }
 }
 
+const char *detail::debugType(const char *Filename) {
+  if (const char *Slash = strrchr(Filename, '/'))
+    return Slash + 1;
+  if (const char *Backslash = strrchr(Filename, '\\'))
+    return Backslash + 1;
+  return Filename;
+}
+
 } // namespace clangd
 } // namespace clang
Index: clangd/JSONRPCDispatcher.h
===================================================================
--- clangd/JSONRPCDispatcher.h
+++ clangd/JSONRPCDispatcher.h
@@ -30,14 +30,16 @@
   // JSONOutput now that we pass Context everywhere.
 public:
   JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs,
-             llvm::raw_ostream *InputMirror = nullptr, bool Pretty = false)
-      : Pretty(Pretty), Outs(Outs), Logs(Logs), InputMirror(InputMirror) {}
+             Logger::Level MinLevel, llvm::raw_ostream *InputMirror = nullptr,
+             bool Pretty = false)
+      : Pretty(Pretty), MinLevel(MinLevel), Outs(Outs), Logs(Logs),
+        InputMirror(InputMirror) {}
 
   /// Emit a JSONRPC message.
   void writeMessage(const json::Expr &Result);
 
   /// Write a line to the logging stream.
-  void log(const Twine &Message) override;
+  void log(Level, const llvm::formatv_object_base &Message) override;
 
   /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is
   /// null.
@@ -48,6 +50,7 @@
   const bool Pretty;
 
 private:
+  Logger::Level MinLevel;
   llvm::raw_ostream &Outs;
   llvm::raw_ostream &Logs;
   llvm::raw_ostream *InputMirror;
Index: clangd/JSONRPCDispatcher.cpp
===================================================================
--- clangd/JSONRPCDispatcher.cpp
+++ clangd/JSONRPCDispatcher.cpp
@@ -15,6 +15,7 @@
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Chrono.h"
 #include "llvm/Support/Errno.h"
+#include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/SourceMgr.h"
 #include <istream>
 
@@ -67,14 +68,18 @@
     Outs << "Content-Length: " << S.size() << "\r\n\r\n" << S;
     Outs.flush();
   }
-  log(llvm::Twine("--> ") + S + "\n");
+  vlog("--> {0}\n", S);
 }
 
-void JSONOutput::log(const Twine &Message) {
+void JSONOutput::log(Logger::Level Level,
+                     const llvm::formatv_object_base &Message) {
+  if (Level < MinLevel)
+    return;
   llvm::sys::TimePoint<> Timestamp = std::chrono::system_clock::now();
   trace::log(Message);
   std::lock_guard<std::mutex> Guard(StreamMutex);
-  Logs << llvm::formatv("[{0:%H:%M:%S.%L}] {1}\n", Timestamp, Message);
+  Logs << llvm::formatv("{0}[{1:%H:%M:%S.%L}] {2}\n", indicator(Level),
+                        Timestamp, Message);
   Logs.flush();
 }
 
@@ -89,7 +94,7 @@
 void clangd::reply(json::Expr &&Result) {
   auto ID = Context::current().get(RequestID);
   if (!ID) {
-    log("Attempted to reply to a notification!");
+    elog("Attempted to reply to a notification!");
     return;
   }
   RequestSpan::attach([&](json::obj &Args) { Args["Reply"] = Result; });
@@ -103,7 +108,7 @@
 }
 
 void clangd::replyError(ErrorCode code, const llvm::StringRef &Message) {
-  log("Error " + Twine(static_cast<int>(code)) + ": " + Message);
+  elog("Error {0}: {1}", static_cast<int>(code), Message);
   RequestSpan::attach([&](json::obj &Args) {
     Args["Error"] =
         json::obj{{"code", static_cast<int>(code)}, {"message", Message.str()}};
@@ -228,9 +233,9 @@
     // Content-Length is a mandatory header, and the only one we handle.
     if (LineRef.consume_front("Content-Length: ")) {
       if (ContentLength != 0) {
-        log("Warning: Duplicate Content-Length header received. "
-            "The previous value for this message (" +
-            llvm::Twine(ContentLength) + ") was ignored.");
+        elog("Warning: Duplicate Content-Length header received. "
+             "The previous value for this message ({0}) was ignored.",
+             ContentLength);
       }
       llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
       continue;
@@ -246,8 +251,9 @@
 
   // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999"
   if (ContentLength > 1 << 30) { // 1024M
-    log("Refusing to read message with long Content-Length: " +
-        Twine(ContentLength) + ". Expect protocol errors.");
+    elog("Refusing to read message with long Content-Length: {0}. "
+         "Expect protocol errors",
+         ContentLength);
     return llvm::None;
   }
   if (ContentLength == 0) {
@@ -262,8 +268,8 @@
                                        ContentLength - Pos, In);
     Out.mirrorInput(StringRef(&JSON[Pos], Read));
     if (Read == 0) {
-      log("Input was aborted. Read only " + llvm::Twine(Pos) +
-          " bytes of expected " + llvm::Twine(ContentLength) + ".");
+      elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
+           ContentLength);
       return llvm::None;
     }
     clearerr(In); // If we're done, the error was transient. If we're not done,
@@ -295,7 +301,7 @@
   }
 
   if (ferror(In)) {
-    log("Input error while reading message!");
+    elog("Input error while reading message!");
     return llvm::None;
   } else { // Including EOF
     Out.mirrorInput(
@@ -319,21 +325,20 @@
       (InputStyle == Delimited) ? readDelimitedMessage : readStandardMessage;
   while (!IsDone && !feof(In)) {
     if (ferror(In)) {
-      log("IO error: " + llvm::sys::StrError());
+      elog("IO error: {0}", llvm::sys::StrError());
       return;
     }
     if (auto JSON = ReadMessage(In, Out)) {
       if (auto Doc = json::parse(*JSON)) {
         // Log the formatted message.
-        log(llvm::formatv(Out.Pretty ? "<-- {0:2}\n" : "<-- {0}\n", *Doc));
+        vlog(Out.Pretty ? "<-- {0:2}\n" : "<-- {0}\n", *Doc);
         // Finally, execute the action for this JSON message.
         if (!Dispatcher.call(*Doc, Out))
-          log("JSON dispatch failed!");
+          elog("JSON dispatch failed!");
       } else {
         // Parse error. Log the raw message.
-        log(llvm::formatv("<-- {0}\n" , *JSON));
-        log(llvm::Twine("JSON parse error: ") +
-            llvm::toString(Doc.takeError()));
+        vlog("<-- {0}\n", *JSON);
+        elog("JSON parse error: {0}", llvm::toString(Doc.takeError()));
       }
     }
   }
Index: clangd/GlobalCompilationDatabase.cpp
===================================================================
--- clangd/GlobalCompilationDatabase.cpp
+++ clangd/GlobalCompilationDatabase.cpp
@@ -47,7 +47,7 @@
       return std::move(Candidates.front());
     }
   } else {
-    log("Failed to find compilation database for " + Twine(File));
+    log("Failed to find compilation database for {0}", File);
   }
   return llvm::None;
 }
Index: clangd/FindSymbols.cpp
===================================================================
--- clangd/FindSymbols.cpp
+++ clangd/FindSymbols.cpp
@@ -121,16 +121,15 @@
     auto &CD = Sym.Definition ? Sym.Definition : Sym.CanonicalDeclaration;
     auto Uri = URI::parse(CD.FileURI);
     if (!Uri) {
-      log(llvm::formatv(
-          "Workspace symbol: Could not parse URI '{0}' for symbol '{1}'.",
-          CD.FileURI, Sym.Name));
+      log("Workspace symbol: Could not parse URI '{0}' for symbol '{1}'.",
+          CD.FileURI, Sym.Name);
       return;
     }
     auto Path = URI::resolve(*Uri, HintPath);
     if (!Path) {
-      log(llvm::formatv("Workspace symbol: Could not resolve path for URI "
-                        "'{0}' for symbol '{1}'.",
-                        (*Uri).toString(), Sym.Name.str()));
+      log("Workspace symbol: Could not resolve path for URI '{0}' for symbol "
+          "'{1}'.",
+          Uri->toString(), Sym.Name);
       return;
     }
     Location L;
@@ -154,16 +153,15 @@
     if (auto NameMatch = Filter.match(Sym.Name))
       Relevance.NameMatch = *NameMatch;
     else {
-      log(llvm::formatv("Workspace symbol: {0} didn't match query {1}",
-                        Sym.Name, Filter.pattern()));
+      log("Workspace symbol: {0} didn't match query {1}", Sym.Name,
+          Filter.pattern());
       return;
     }
     Relevance.merge(Sym);
     auto Score =
         evaluateSymbolAndRelevance(Quality.evaluate(), Relevance.evaluate());
-    LLVM_DEBUG(llvm::dbgs() << "FindSymbols: " << Sym.Scope << Sym.Name << " = "
-                            << Score << "\n"
-                            << Quality << Relevance << "\n");
+    dlog("FindSymbols: {0}{1} = {2}\n{3}{4}\n", Sym.Scope, Sym.Name, Score,
+         Quality, Relevance);
 
     Top.push({Score, std::move(Info)});
   });
Index: clangd/FileDistance.cpp
===================================================================
--- clangd/FileDistance.cpp
+++ clangd/FileDistance.cpp
@@ -35,7 +35,6 @@
 #include "Logger.h"
 #include "llvm/ADT/STLExtras.h"
 #include <queue>
-#define DEBUG_TYPE "FileDistance"
 
 namespace clang {
 namespace clangd {
@@ -64,8 +63,8 @@
   // Keep track of down edges, in case we can use them to improve on this.
   for (const auto &S : Sources) {
     auto Canonical = canonicalize(S.getKey());
-    LLVM_DEBUG(dbgs() << "Source " << Canonical << " = " << S.second.Cost
-                      << ", MaxUp=" << S.second.MaxUpTraversals << "\n");
+    dlog("Source {0} = {1}, MaxUp = {2}", Canonical, S.second.Cost,
+         S.second.MaxUpTraversals);
     // Walk up to ancestors of this source, assigning cost.
     StringRef Rest = Canonical;
     llvm::hash_code Hash = hash_value(Rest);
@@ -134,21 +133,19 @@
       Cost += Opts.DownCost;
     Cache.try_emplace(Hash, Cost);
   }
-  LLVM_DEBUG(dbgs() << "distance(" << Path << ") = " << Cost << "\n");
+  dlog("distance({0} = {1})", Path, Cost);
   return Cost;
 }
 
 unsigned URIDistance::distance(llvm::StringRef URI) {
   auto R = Cache.try_emplace(llvm::hash_value(URI), FileDistance::kUnreachable);
   if (!R.second)
     return R.first->getSecond();
   if (auto U = clangd::URI::parse(URI)) {
-    LLVM_DEBUG(dbgs() << "distance(" << URI << ") = distance(" << U->body()
-                      << ")\n");
+    dlog("distance({0} = {1})", URI, U->body());
     R.first->second = forScheme(U->scheme()).distance(U->body());
   } else {
-    log("URIDistance::distance() of unparseable " + URI + ": " +
-        llvm::toString(U.takeError()));
+    log("URIDistance::distance() of unparseable {0}: {1}", URI, U.takeError());
   }
   return R.first->second;
 }
@@ -163,9 +160,8 @@
       else
         consumeError(U.takeError());
     }
-    LLVM_DEBUG(dbgs() << "FileDistance for scheme " << Scheme << ": "
-                      << SchemeSources.size() << "/" << Sources.size()
-                      << " sources\n");
+    dlog("FileDistance for scheme {0}: {1}/{2} sources", Scheme,
+         SchemeSources.size(), Sources.size());
     Delegate.reset(new FileDistance(std::move(SchemeSources), Opts));
   }
   return *Delegate;
Index: clangd/Diagnostics.cpp
===================================================================
--- clangd/Diagnostics.cpp
+++ clangd/Diagnostics.cpp
@@ -375,7 +375,7 @@
   if (mentionsMainFile(*LastDiag))
     Output.push_back(std::move(*LastDiag));
   else
-    log(Twine("Dropped diagnostic outside main file:") + LastDiag->File + ":" +
+    log("Dropped diagnostic outside main file: {0}: {1}", LastDiag->File,
         LastDiag->Message);
   LastDiag.reset();
 }
Index: clangd/Compiler.cpp
===================================================================
--- clangd/Compiler.cpp
+++ clangd/Compiler.cpp
@@ -31,7 +31,7 @@
     OS << ":";
   }
 
-  clangd::log(llvm::formatv("Ignored diagnostic. {0}{1}", Location, Message));
+  clangd::log("Ignored diagnostic. {0}{1}", Location, Message);
 }
 
 void IgnoreDiagnostics::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
Index: clangd/CodeComplete.h
===================================================================
--- clangd/CodeComplete.h
+++ clangd/CodeComplete.h
@@ -67,6 +67,9 @@
     std::string NoInsert = " ";
   } IncludeIndicator;
 
+  /// Expose origins of completion items in the label (for debugging).
+  bool ShowOrigins = false;
+
   // Populated internally by clangd, do not set.
   /// If `Index` is set, it is used to augment the code completion
   /// results.
@@ -103,6 +106,7 @@
   //  - Documentation may be from one symbol, or a combination of several
   // Other fields should apply equally to all bundled completions.
   unsigned BundleSize = 1;
+  SymbolOrigin Origin = SymbolOrigin::Unknown;
   // The header through which this symbol could be included.
   // Quoted string as expected by an #include directive, e.g. "<memory>".
   // Empty for non-symbol completions, or when not known.
Index: clangd/CodeComplete.cpp
===================================================================
--- clangd/CodeComplete.cpp
+++ clangd/CodeComplete.cpp
@@ -41,6 +41,7 @@
 #include "clang/Sema/Sema.h"
 #include "clang/Tooling/Core/Replacement.h"
 #include "llvm/Support/Format.h"
+#include "llvm/Support/ScopedPrinter.h"
 #include <queue>
 
 // We log detailed candidate here if you run with -debug-only=codecomplete.
@@ -266,6 +267,8 @@
       : ASTCtx(ASTCtx), ExtractDocumentation(Opts.IncludeComments) {
     add(C, SemaCCS);
     if (C.SemaResult) {
+      Completion.Origin =
+          static_cast<SymbolOrigin>(Completion.Origin | SymbolOrigin::AST);
       Completion.Name = llvm::StringRef(SemaCCS->getTypedText());
       if (Completion.Scope.empty())
         if (C.SemaResult->Kind == CodeCompletionResult::RK_Declaration)
@@ -277,6 +280,8 @@
           toCompletionItemKind(C.SemaResult->Kind, C.SemaResult->Declaration);
     }
     if (C.IndexResult) {
+      Completion.Origin =
+          static_cast<SymbolOrigin>(Completion.Origin | C.IndexResult->Origin);
       if (Completion.Scope.empty())
         Completion.Scope = C.IndexResult->Scope;
       if (Completion.Kind == CompletionItemKind::Missing)
@@ -304,11 +309,10 @@
         if (Include->second)
           Completion.HeaderInsertion = Includes.insert(Include->first);
       } else
-        log(llvm::formatv(
-            "Failed to generate include insertion edits for adding header "
+        log("Failed to generate include insertion edits for adding header "
             "(FileURI='{0}', IncludeHeader='{1}') into {2}",
             C.IndexResult->CanonicalDeclaration.FileURI,
-            C.IndexResult->Detail->IncludeHeader, FileName));
+            C.IndexResult->Detail->IncludeHeader, FileName);
     }
   }
 
@@ -589,11 +593,10 @@
     if (NumResults == 0 && !contextAllowsIndex(Context.getKind()))
       return;
     if (CCSema) {
-      log(llvm::formatv(
-          "Multiple code complete callbacks (parser backtracked?). "
+      log("Multiple code complete callbacks (parser backtracked?). "
           "Dropping results from context {0}, keeping results from {1}.",
           getCompletionKindString(Context.getKind()),
-          getCompletionKindString(this->CCContext.getKind())));
+          getCompletionKindString(this->CCContext.getKind()));
       return;
     }
     // Record the completion context.
@@ -794,7 +797,7 @@
                                           &DummyDiagsConsumer, false),
       Input.VFS);
   if (!CI) {
-    log("Couldn't create CompilerInvocation");
+    elog("Couldn't create CompilerInvocation");
     return false;
   }
   auto &FrontendOpts = CI->getFrontendOpts();
@@ -808,8 +811,7 @@
   FrontendOpts.CodeCompletionAt.FileName = Input.FileName;
   auto Offset = positionToOffset(Input.Contents, Input.Pos);
   if (!Offset) {
-    log("Code completion position was invalid " +
-        llvm::toString(Offset.takeError()));
+    elog("Code completion position was invalid {0}", Offset.takeError());
     return false;
   }
   std::tie(FrontendOpts.CodeCompletionAt.Line,
@@ -832,15 +834,15 @@
 
   SyntaxOnlyAction Action;
   if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) {
-    log("BeginSourceFile() failed when running codeComplete for " +
+    log("BeginSourceFile() failed when running codeComplete for {0}",
         Input.FileName);
     return false;
   }
   if (Includes)
     Clang->getPreprocessor().addPPCallbacks(
         collectIncludeStructureCallback(Clang->getSourceManager(), Includes));
   if (!Action.Execute()) {
-    log("Execute() failed when running codeComplete for " + Input.FileName);
+    log("Execute() failed when running codeComplete for {0}", Input.FileName);
     return false;
   }
   Action.EndSourceFile();
@@ -960,8 +962,8 @@
                            format::DefaultFallbackStyle, SemaCCInput.Contents,
                            SemaCCInput.VFS.get());
       if (!Style) {
-        log("Failed to get FormatStyle for file" + SemaCCInput.FileName + ": " +
-            llvm::toString(Style.takeError()) + ". Fallback is LLVM style.");
+        log("getStyle() failed for file {0}: {1}. Fallback is LLVM style.",
+            SemaCCInput.FileName, Style.takeError());
         Style = format::getLLVMStyle();
       }
       // If preprocessor was run, inclusions from preprocessor callback should
@@ -1007,10 +1009,10 @@
     SPAN_ATTACH(Tracer, "merged_results", NBoth);
     SPAN_ATTACH(Tracer, "returned_results", Output.Completions.size());
     SPAN_ATTACH(Tracer, "incomplete", Output.HasMore);
-    log(llvm::formatv("Code complete: {0} results from Sema, {1} from Index, "
-                      "{2} matched, {3} returned{4}.",
-                      NSema, NIndex, NBoth, Output.Completions.size(),
-                      Output.HasMore ? " (incomplete)" : ""));
+    log("Code complete: {0} results from Sema, {1} from Index, "
+        "{2} matched, {3} returned{4}.",
+        NSema, NIndex, NBoth, Output.Completions.size(),
+        Output.HasMore ? " (incomplete)" : "");
     assert(!Opts.Limit || Output.Completions.size() <= Opts.Limit);
     // We don't assert that isIncomplete means we hit a limit.
     // Indexes may choose to impose their own limits even if we don't have one.
@@ -1058,9 +1060,8 @@
                                 Recorder->CCSema->getSourceManager());
     // FIXME: we should send multiple weighted paths here.
     Req.ProximityPaths.push_back(FileName);
-    log(llvm::formatv("Code complete: fuzzyFind(\"{0}\", scopes=[{1}])",
-                      Req.Query,
-                      llvm::join(Req.Scopes.begin(), Req.Scopes.end(), ",")));
+    vlog("Code complete: fuzzyFind(\"{0}\", scopes=[{1}])", Req.Query,
+         llvm::join(Req.Scopes.begin(), Req.Scopes.end(), ","));
     // Run the query against the index.
     if (Opts.Index->fuzzyFind(
             Req, [&](const Symbol &Sym) { ResultsBuilder.insert(Sym); }))
@@ -1137,24 +1138,24 @@
     SymbolQualitySignals Quality;
     SymbolRelevanceSignals Relevance;
     Relevance.Query = SymbolRelevanceSignals::CodeComplete;
-    // FIXME: re-enable this after working out why it eats memory.
-    // Relevance.FileProximityMatch = FileProximity.getPointer();
+    Relevance.FileProximityMatch = FileProximity.getPointer();
     auto &First = Bundle.front();
     if (auto FuzzyScore = fuzzyScore(First))
       Relevance.NameMatch = *FuzzyScore;
     else
       return;
-    unsigned SemaResult = 0, IndexResult = 0;
+    SymbolOrigin Origin = SymbolOrigin::Unknown;
     for (const auto &Candidate : Bundle) {
       if (Candidate.IndexResult) {
         Quality.merge(*Candidate.IndexResult);
         Relevance.merge(*Candidate.IndexResult);
-        ++IndexResult;
+        Origin =
+            static_cast<SymbolOrigin>(Origin | Candidate.IndexResult->Origin);
       }
       if (Candidate.SemaResult) {
         Quality.merge(*Candidate.SemaResult);
         Relevance.merge(*Candidate.SemaResult);
-        ++SemaResult;
+        Origin = static_cast<SymbolOrigin>(Origin | SymbolOrigin::AST);
       }
     }
 
@@ -1167,15 +1168,13 @@
                                ? Scores.Total / Relevance.NameMatch
                                : Scores.Quality;
 
-    LLVM_DEBUG(llvm::dbgs() << "CodeComplete: " << First.Name << "("
-                            << IndexResult << " index) "
-                            << "(" << SemaResult << " sema)"
-                            << " = " << Scores.Total << "\n"
-                            << Quality << Relevance << "\n");
+    dlog("CodeComplete: {0} ({1}) = {2}\n{3}{4}\n", First.Name,
+         llvm::to_string(Origin), Scores.Total, llvm::to_string(Quality),
+         llvm::to_string(Relevance));
 
-    NSema += bool(SemaResult);
-    NIndex += bool(IndexResult);
-    NBoth += SemaResult && IndexResult;
+    NSema += bool(Origin & SymbolOrigin::AST);
+    NIndex += bool(Origin & ~SymbolOrigin::AST);
+    NBoth += (Origin & SymbolOrigin::AST) && (Origin & ~SymbolOrigin::AST);
     if (Candidates.push({std::move(Bundle), Scores}))
       Incomplete = true;
   }
@@ -1243,7 +1242,9 @@
   CompletionItem LSP;
   LSP.label = (HeaderInsertion ? Opts.IncludeIndicator.Insert
                                : Opts.IncludeIndicator.NoInsert) +
+              (Opts.ShowOrigins ? "[" + llvm::to_string(Origin) + "]" : "") +
               RequiredQualifier + Name + Signature;
+
   LSP.kind = Kind;
   LSP.detail = BundleSize > 1 ? llvm::formatv("[{0} overloads]", BundleSize)
                               : ReturnType;
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -146,7 +146,7 @@
   auto Action = llvm::make_unique<ClangdFrontendAction>();
   const FrontendInputFile &MainInput = Clang->getFrontendOpts().Inputs[0];
   if (!Action->BeginSourceFile(*Clang, MainInput)) {
-    log("BeginSourceFile() failed when building AST for " +
+    log("BeginSourceFile() failed when building AST for {0}",
         MainInput.getFile());
     return llvm::None;
   }
@@ -158,7 +158,7 @@
       collectIncludeStructureCallback(Clang->getSourceManager(), &Includes));
 
   if (!Action->Execute())
-    log("Execute() failed when building AST for " + MainInput.getFile());
+    log("Execute() failed when building AST for {0}", MainInput.getFile());
 
   // UnitDiagsConsumer is local, we can not store it in CompilerInstance that
   // has a longer lifetime.
@@ -306,11 +306,11 @@
       compileCommandsAreEqual(Inputs.CompileCommand, OldCompileCommand) &&
       OldPreamble->Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
                                      Inputs.FS.get())) {
-    log("Reusing preamble for file " + Twine(FileName));
+    vlog("Reusing preamble for file {0}", Twine(FileName));
     return OldPreamble;
   }
-  log("Preamble for file " + Twine(FileName) +
-      " cannot be reused. Attempting to rebuild it.");
+  vlog("Preamble for file {0} cannot be reused. Attempting to rebuild it.",
+       FileName);
 
   trace::Span Tracer("BuildPreamble");
   SPAN_ATTACH(Tracer, "File", FileName);
@@ -339,13 +339,13 @@
   CI.getFrontendOpts().SkipFunctionBodies = false;
 
   if (BuiltPreamble) {
-    log("Built preamble of size " + Twine(BuiltPreamble->getSize()) +
-        " for file " + Twine(FileName));
+    vlog("Built preamble of size {0} for file {1}", BuiltPreamble->getSize(),
+         FileName);
     return std::make_shared<PreambleData>(
         std::move(*BuiltPreamble), PreambleDiagnostics.take(),
         SerializedDeclsCollector.takeIncludes());
   } else {
-    log("Could not build a preamble for file " + Twine(FileName));
+    elog("Could not build a preamble for file {0}", FileName);
     return nullptr;
   }
 }
@@ -375,7 +375,7 @@
   const SourceManager &SourceMgr = AST.getSourceManager();
   auto Offset = positionToOffset(SourceMgr.getBufferData(FID), Pos);
   if (!Offset) {
-    log("getBeginningOfIdentifier: " + toString(Offset.takeError()));
+    log("getBeginningOfIdentifier: {0}", Offset.takeError());
     return SourceLocation();
   }
   SourceLocation InputLoc = SourceMgr.getComposedLoc(FID, *Offset);
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -118,12 +118,12 @@
   auto FS = FSProvider.getFileSystem();
   auto Status = FS->status(RootPath);
   if (!Status)
-    log("Failed to get status for RootPath " + RootPath + ": " +
-        Status.getError().message());
+    elog("Failed to get status for RootPath {0}: {1}", RootPath,
+         Status.getError().message());
   else if (Status->isDirectory())
     this->RootPath = RootPath;
   else
-    log("The provided RootPath " + RootPath + " is not a directory.");
+    elog("The provided RootPath {0} is not a directory.", RootPath);
 }
 
 void ClangdServer::addDocument(PathRef File, StringRef Contents,
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -158,7 +158,7 @@
     DraftMgr.removeDraft(File);
     Server.removeDocument(File);
     CDB.invalidate(File);
-    log(llvm::toString(Contents.takeError()));
+    elog("Failed to update {0}: {1}", File, Contents.takeError());
     return;
   }
 
Index: clang-tidy/misc/UnusedParametersCheck.cpp
===================================================================
--- clang-tidy/misc/UnusedParametersCheck.cpp
+++ clang-tidy/misc/UnusedParametersCheck.cpp
@@ -159,8 +159,9 @@
       MyDiag << removeParameter(Result, FD, ParamIndex);
 
   // Fix all call sites.
-  for (const auto *Call : Indexer->getFnCalls(Function))
-    MyDiag << removeArgument(Result, Call, ParamIndex);
+  for (const CallExpr *Call : Indexer->getFnCalls(Function))
+    if (ParamIndex < Call->getNumArgs()) // See PR38055 for example.
+      MyDiag << removeArgument(Result, Call, ParamIndex);
 }
 
 void UnusedParametersCheck::check(const MatchFinder::MatchResult &Result) {
Index: clang-tidy/cert/ProperlySeededRandomGeneratorCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/cert/ProperlySeededRandomGeneratorCheck.h
@@ -0,0 +1,47 @@
+//===--- ProperlySeededRandomGeneratorCheck.h - clang-tidy-------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_PROPERLY_SEEDED_RANDOM_GENERATOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_PROPERLY_SEEDED_RANDOM_GENERATOR_H
+
+#include "../ClangTidy.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Random number generator must be seeded properly.
+///
+/// A random number generator initialized with default value or a
+/// constant expression is a security vulnerability.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-properly-seeded-random-generator.html
+class ProperlySeededRandomGeneratorCheck : public ClangTidyCheck {
+public:
+  ProperlySeededRandomGeneratorCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  template <class T>
+  void checkSeed(const ast_matchers::MatchFinder::MatchResult &Result,
+                 const T *Func);
+
+  std::string RawDisallowedSeedTypes;
+  SmallVector<StringRef, 5> DisallowedSeedTypes;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_PROPERLY_SEEDED_RANDOM_GENERATOR_H
Index: clang-tidy/cert/ProperlySeededRandomGeneratorCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/cert/ProperlySeededRandomGeneratorCheck.cpp
@@ -0,0 +1,124 @@
+//===--- ProperlySeededRandomGeneratorCheck.cpp - clang-tidy---------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProperlySeededRandomGeneratorCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+ProperlySeededRandomGeneratorCheck::ProperlySeededRandomGeneratorCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      RawDisallowedSeedTypes(
+          Options.get("DisallowedSeedTypes", "time_t,std::time_t")) {
+  StringRef(RawDisallowedSeedTypes).split(DisallowedSeedTypes, ',');
+}
+
+void ProperlySeededRandomGeneratorCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "DisallowedSeedTypes", RawDisallowedSeedTypes);
+}
+
+void ProperlySeededRandomGeneratorCheck::registerMatchers(MatchFinder *Finder) {
+  auto RandomGeneratorEngineDecl = cxxRecordDecl(hasAnyName(
+      "::std::linear_congruential_engine", "::std::mersenne_twister_engine",
+      "::std::subtract_with_carry_engine", "::std::discard_block_engine",
+      "::std::independent_bits_engine", "::std::shuffle_order_engine"));
+  auto RandomGeneratorEngineTypeMatcher = hasType(hasUnqualifiedDesugaredType(
+      recordType(hasDeclaration(RandomGeneratorEngineDecl))));
+
+  // std::mt19937 engine;
+  // engine.seed();
+  //        ^
+  // engine.seed(1);
+  //        ^
+  // const int x = 1;
+  // engine.seed(x);
+  //        ^
+  Finder->addMatcher(
+      cxxMemberCallExpr(
+          has(memberExpr(has(declRefExpr(RandomGeneratorEngineTypeMatcher)),
+                         member(hasName("seed")),
+                         unless(hasDescendant(cxxThisExpr())))))
+          .bind("seed"),
+      this);
+
+  // std::mt19937 engine;
+  //              ^
+  // std::mt19937 engine(1);
+  //              ^
+  // const int x = 1;
+  // std::mt19937 engine(x);
+  //              ^
+  Finder->addMatcher(
+      cxxConstructExpr(RandomGeneratorEngineTypeMatcher).bind("ctor"), this);
+
+  // srand();
+  // ^
+  // const int x = 1;
+  // srand(x);
+  // ^
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasAnyName("::srand", "::std::srand"))))
+          .bind("srand"),
+      this);
+}
+
+void ProperlySeededRandomGeneratorCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
+  if (Ctor)
+    checkSeed(Result, Ctor);
+
+  const auto *Func = Result.Nodes.getNodeAs<CXXMemberCallExpr>("seed");
+  if (Func)
+    checkSeed(Result, Func);
+
+  const auto *Srand = Result.Nodes.getNodeAs<CallExpr>("srand");
+  if (Srand)
+    checkSeed(Result, Srand);
+}
+
+template <class T>
+void ProperlySeededRandomGeneratorCheck::checkSeed(
+    const MatchFinder::MatchResult &Result, const T *Func) {
+  if (Func->getNumArgs() == 0 || Func->getArg(0)->isDefaultArgument()) {
+    diag(Func->getExprLoc(),
+         "random number generator seeded with a default argument will generate "
+         "a predictable sequence of values");
+    return;
+  }
+
+  llvm::APSInt Value;
+  if (Func->getArg(0)->EvaluateAsInt(Value, *Result.Context)) {
+    diag(Func->getExprLoc(),
+         "random number generator seeded with a constant value will generate a "
+         "predictable sequence of values");
+    return;
+  }
+
+  const std::string SeedType(
+      Func->getArg(0)->IgnoreCasts()->getType().getAsString());
+  if (llvm::find(DisallowedSeedTypes, SeedType) != DisallowedSeedTypes.end()) {
+    diag(Func->getExprLoc(),
+         "random number generator seeded with a disallowed source of seed "
+         "value will generate a predictable sequence of values");
+    return;
+  }
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/cert/CMakeLists.txt
===================================================================
--- clang-tidy/cert/CMakeLists.txt
+++ clang-tidy/cert/CMakeLists.txt
@@ -7,6 +7,7 @@
   FloatLoopCounter.cpp
   LimitedRandomnessCheck.cpp
   PostfixOperatorCheck.cpp
+  ProperlySeededRandomGeneratorCheck.cpp
   SetLongJmpCheck.cpp
   StaticObjectExceptionCheck.cpp
   StrToNumCheck.cpp
Index: clang-tidy/cert/CERTTidyModule.cpp
===================================================================
--- clang-tidy/cert/CERTTidyModule.cpp
+++ clang-tidy/cert/CERTTidyModule.cpp
@@ -21,6 +21,7 @@
 #include "FloatLoopCounter.h"
 #include "LimitedRandomnessCheck.h"
 #include "PostfixOperatorCheck.h"
+#include "ProperlySeededRandomGeneratorCheck.h"
 #include "SetLongJmpCheck.h"
 #include "StaticObjectExceptionCheck.h"
 #include "StrToNumCheck.h"
@@ -58,6 +59,8 @@
         "cert-err61-cpp");
     // MSC
     CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc50-cpp");
+    CheckFactories.registerCheck<ProperlySeededRandomGeneratorCheck>(
+        "cert-msc51-cpp");
 
     // C checkers
     // DCL
@@ -72,6 +75,8 @@
     CheckFactories.registerCheck<StrToNumCheck>("cert-err34-c");
     // MSC
     CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc30-c");
+    CheckFactories.registerCheck<ProperlySeededRandomGeneratorCheck>(
+        "cert-msc32-c");
   }
 };
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to