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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits