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("cert-msc50-cpp"); + CheckFactories.registerCheck( + "cert-msc51-cpp"); // C checkers // DCL @@ -72,6 +75,8 @@ CheckFactories.registerCheck("cert-err34-c"); // MSC CheckFactories.registerCheck("cert-msc30-c"); + CheckFactories.registerCheck( + "cert-msc32-c"); } }; 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/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 + +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 + void checkSeed(const ast_matchers::MatchFinder::MatchResult &Result, + const T *Func); + + std::string RawDisallowedSeedTypes; + SmallVector 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("ctor"); + if (Ctor) + checkSeed(Result, Ctor); + + const auto *Func = Result.Nodes.getNodeAs("seed"); + if (Func) + checkSeed(Result, Func); + + const auto *Srand = Result.Nodes.getNodeAs("srand"); + if (Srand) + checkSeed(Result, Srand); +} + +template +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/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: 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: 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/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -146,7 +146,7 @@ auto Action = llvm::make_unique(); 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( 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/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. "". // 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 // 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(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(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,7 +834,7 @@ 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; } @@ -840,7 +842,7 @@ 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(Origin | Candidate.IndexResult->Origin); } if (Candidate.SemaResult) { Quality.merge(*Candidate.SemaResult); Relevance.merge(*Candidate.SemaResult); - ++SemaResult; + Origin = static_cast(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/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/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/FileDistance.cpp =================================================================== --- clangd/FileDistance.cpp +++ clangd/FileDistance.cpp @@ -35,7 +35,6 @@ #include "Logger.h" #include "llvm/ADT/STLExtras.h" #include -#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,7 +133,7 @@ Cost += Opts.DownCost; Cache.try_emplace(Hash, Cost); } - LLVM_DEBUG(dbgs() << "distance(" << Path << ") = " << Cost << "\n"); + dlog("distance({0} = {1})", Path, Cost); return Cost; } @@ -143,12 +142,10 @@ 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/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/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/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 @@ -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 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(code)) + ": " + Message); + elog("Error {0}: {1}", static_cast(code), Message); RequestSpan::attach([&](json::obj &Args) { Args["Error"] = json::obj{{"code", static_cast(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/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 void elog(const char *Fmt, Ts &&... Vals) { + detail::log(Logger::Error, llvm::formatv(Fmt, std::forward(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 void log(const char *Fmt, Ts &&... Vals) { + detail::log(Logger::Info, llvm::formatv(Fmt, std::forward(Vals)...)); +} +// vlog() is used for details often needed for debugging clangd sessions. +// This level would typically be enabled for clangd developers. +template void vlog(const char *Fmt, Ts &&... Vals) { + detail::log(Logger::Verbose, llvm::formatv(Fmt, std::forward(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,9 +25,10 @@ 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 Guard(Mu); @@ -35,5 +36,13 @@ } } +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/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/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/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(D)) if (R->isInjectedClassName()) DC = DC->getParent(); + // Class constructor should have the same scope as the class. + if (const auto *Ctor = llvm::dyn_cast(D)) + DC = DC->getParent(); bool InClass = false; for (; !DC->isFileContext(); DC = DC->getParent()) { if (DC->isFunctionOrMethod()) 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 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/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/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(); addSystemHeadersMapping(Includes.get()); CollectorOpts.Includes = Includes.get(); 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/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(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/Merge.cpp =================================================================== --- clangd/index/Merge.cpp +++ clangd/index/Merge.cpp @@ -115,6 +115,9 @@ } else S.Detail = O.Detail; } + + S.Origin = + static_cast(S.Origin | O.Origin | SymbolOrigin::Merge); return S; } 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/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 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 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 + ShowOrigins("debug-origin", + llvm::cl::desc("Show origins of completion items"), + llvm::cl::init(clangd::CodeCompleteOptions().ShowOrigins), + llvm::cl::Hidden); + static llvm::cl::opt 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: 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 + `_ check + + Detects inappropriate seeding of C++ random generators and C ``srand()`` function. + +- New `cert-msc32-c + `_ check + + Detects inappropriate seeding of ``srand()`` function. + + Improvements to include-fixer ----------------------------- 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 `_ for more information. 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 +`_ and +`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/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-flp30-c cert-msc30-c (redirects to cert-msc50-cpp) + cert-msc32-c (redirects to cert-msc51-cpp) cert-msc50-cpp + cert-msc51-cpp cert-oop11-cpp (redirects to performance-move-constructor-init) cppcoreguidelines-avoid-goto cppcoreguidelines-c-copy-assignment-signature (redirects to misc-unconventional-assign-operator) 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: 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 +struct linear_congruential_engine { + linear_congruential_engine(int _ = 0); + void seed(int _ = 0); +}; +using default_random_engine = linear_congruential_engine; + +using size_t = int; +template +struct mersenne_twister_engine { + mersenne_twister_engine(int _ = 0); + void seed(int _ = 0); +}; +using mt19937 = mersenne_twister_engine; + +template +struct subtract_with_carry_engine { + subtract_with_carry_engine(int _ = 0); + void seed(int _ = 0); +}; +using ranlux24_base = subtract_with_carry_engine; + +template +struct discard_block_engine { + discard_block_engine(); + discard_block_engine(int _); + void seed(); + void seed(int _); +}; +using ranlux24 = discard_block_engine; + +template +struct independent_bits_engine { + independent_bits_engine(); + independent_bits_engine(int _); + void seed(); + void seed(int _); +}; +using independent_bits = independent_bits_engine; + +template +struct shuffle_order_engine { + shuffle_order_engine(); + shuffle_order_engine(int _); + void seed(); + void seed(int _); +}; +using shuffle_order = shuffle_order_engine; + +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/misc-unused-parameters.cpp =================================================================== --- test/clang-tidy/misc-unused-parameters.cpp +++ test/clang-tidy/misc-unused-parameters.cpp @@ -222,6 +222,21 @@ return Function(); } +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 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: 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 &> 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::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: 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/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(&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/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/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 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 Callback) { struct Visitor : RecursiveASTVisitor { - llvm::StringRef Name; + decltype(Callback) CB; llvm::SmallVector 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