//===-- clang-tools-extra/clang-tidy/NoLintDirectiveHandler.cpp -----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file This file implements the NoLintDirectiveHandler class, which is used /// to locate NOLINT comments in the file being analyzed, to decide whether a /// diagnostic should be suppressed. /// //===----------------------------------------------------------------------===// #include "NoLintDirectiveHandler.h" #include "GlobList.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Tooling/Core/Diagnostic.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" #include <cassert> #include <cstddef> #include <iterator> #include <optional> #include <string> #include <tuple> #include <type_traits> #include <utility> namespace clang::tidy { //===----------------------------------------------------------------------===// // NoLintType //===----------------------------------------------------------------------===// // The type - one of NOLINT[NEXTLINE/BEGIN/END]. enum class NoLintType { … }; // Convert a string like "NOLINTNEXTLINE" to its enum `Type::NoLintNextLine`. // Return `std::nullopt` if the string is unrecognized. static std::optional<NoLintType> strToNoLintType(StringRef Str) { … } //===----------------------------------------------------------------------===// // NoLintToken //===----------------------------------------------------------------------===// // Whitespace within a NOLINT's check list shall be ignored. // "NOLINT( check1, check2 )" is equivalent to "NOLINT(check1,check2)". // Return the check list with all extraneous whitespace removed. static std::string trimWhitespace(StringRef Checks) { … } namespace { // Record the presence of a NOLINT comment - its type, location, checks - // as parsed from the file's character contents. class NoLintToken { … }; } // namespace // Consume the entire buffer and return all `NoLintToken`s that were found. static SmallVector<NoLintToken> getNoLints(StringRef Buffer) { … } //===----------------------------------------------------------------------===// // NoLintBlockToken //===----------------------------------------------------------------------===// namespace { // Represents a source range within a pair of NOLINT(BEGIN/END) comments. class NoLintBlockToken { … }; } // namespace // Match NOLINTBEGINs with their corresponding NOLINTENDs and move them into // `NoLintBlockToken`s. If any BEGINs or ENDs are left over, they are moved to // `UnmatchedTokens`. static SmallVector<NoLintBlockToken> formNoLintBlocks(SmallVector<NoLintToken> NoLints, SmallVectorImpl<NoLintToken> &UnmatchedTokens) { … } //===----------------------------------------------------------------------===// // NoLintDirectiveHandler::Impl //===----------------------------------------------------------------------===// class NoLintDirectiveHandler::Impl { … }; bool NoLintDirectiveHandler::Impl::shouldSuppress( DiagnosticsEngine::Level DiagLevel, const Diagnostic &Diag, StringRef DiagName, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors, bool AllowIO, bool EnableNoLintBlocks) { … } // Look at the macro's spelling location for a NOLINT. If none is found, keep // looking up the call stack. bool NoLintDirectiveHandler::Impl::diagHasNoLintInMacro( const Diagnostic &Diag, StringRef DiagName, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors, bool AllowIO, bool EnableNoLintBlocks) { … } // Look behind and ahead for '\n' characters. These mark the start and end of // this line. static std::pair<size_t, size_t> getLineStartAndEnd(StringRef Buffer, size_t From) { … } // Whether the line has a NOLINT of type = `Type` that can suppress the // diagnostic `DiagName`. static bool lineHasNoLint(StringRef Buffer, std::pair<size_t, size_t> LineStartAndEnd, NoLintType Type, StringRef DiagName) { … } // Whether the provided diagnostic is located within and is suppressible by a // block of NOLINT(BEGIN/END) comments. static bool withinNoLintBlock(ArrayRef<NoLintBlockToken> NoLintBlocks, size_t DiagPos, StringRef DiagName) { … } // Get the file contents as a string. static std::optional<StringRef> getBuffer(const SourceManager &SrcMgr, FileID File, bool AllowIO) { … } // We will check for NOLINTs and NOLINTNEXTLINEs first. Checking for these is // not so expensive (just need to parse the current and previous lines). Only if // that fails do we look for NOLINT(BEGIN/END) blocks (which requires reading // the entire file). bool NoLintDirectiveHandler::Impl::diagHasNoLint( StringRef DiagName, SourceLocation DiagLoc, const SourceManager &SrcMgr, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors, bool AllowIO, bool EnableNoLintBlocks) { … } // Construct a [clang-tidy-nolint] diagnostic to do with the unmatched // NOLINT(BEGIN/END) pair. static tooling::Diagnostic makeNoLintError(const SourceManager &SrcMgr, FileID File, const NoLintToken &NoLint) { … } // Find all NOLINT(BEGIN/END) blocks in a file and store in the cache. void NoLintDirectiveHandler::Impl::generateCache( const SourceManager &SrcMgr, StringRef FileName, FileID File, StringRef Buffer, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors) { … } //===----------------------------------------------------------------------===// // NoLintDirectiveHandler //===----------------------------------------------------------------------===// NoLintDirectiveHandler::NoLintDirectiveHandler() : … { … } NoLintDirectiveHandler::~NoLintDirectiveHandler() = default; bool NoLintDirectiveHandler::shouldSuppress( DiagnosticsEngine::Level DiagLevel, const Diagnostic &Diag, StringRef DiagName, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors, bool AllowIO, bool EnableNoLintBlocks) { … } } // namespace clang::tidy