diff --git a/clang/include/clang/Frontend/SARIFDiagnostic.h b/clang/include/clang/Frontend/SARIFDiagnostic.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Frontend/SARIFDiagnostic.h @@ -0,0 +1,76 @@ +//===--- SARIFDiagnostic.h - SARIF Diagnostic Formatting -----*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This is a utility class that provides support for constructing a SARIF object +// containing diagnostics. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_SARIFDIAGNOSTIC_H +#define LLVM_CLANG_FRONTEND_SARIFDIAGNOSTIC_H + +#include "clang/Basic/Sarif.h" +#include "clang/Frontend/DiagnosticRenderer.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { + +class SARIFDiagnostic : public DiagnosticRenderer { +public: + SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts, + DiagnosticOptions *DiagOpts, SarifDocumentWriter *Writer); + + ~SARIFDiagnostic() = default; + + SARIFDiagnostic &operator=(const SARIFDiagnostic &&) = delete; + SARIFDiagnostic(SARIFDiagnostic &&) = delete; + SARIFDiagnostic &operator=(const SARIFDiagnostic &) = delete; + SARIFDiagnostic(const SARIFDiagnostic &) = delete; + +protected: + void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, StringRef Message, + ArrayRef Ranges, + DiagOrStoredDiag D) override; + + void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef Ranges) override; + + void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level, + SmallVectorImpl &Ranges, + ArrayRef Hints) override {} + + void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override; + + void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, + StringRef ModuleName) override; + + void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc, + StringRef ModuleName) override; + +private: + raw_ostream &OS; + + // Shared between SARIFDiagnosticPrinter and this renderer. + SarifDocumentWriter *Writer; + + SarifResult addLocationToResult(SarifResult Result, FullSourceLoc Loc, + PresumedLoc PLoc, + ArrayRef Ranges, + const Diagnostic &Diag); + + SarifRule addDiagnosticLevelToRule(SarifRule Rule, + DiagnosticsEngine::Level Level); + + llvm::StringRef emitFilename(StringRef Filename, const SourceManager &SM); +}; + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Frontend/SARIFDiagnosticPrinter.h b/clang/include/clang/Frontend/SARIFDiagnosticPrinter.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Frontend/SARIFDiagnosticPrinter.h @@ -0,0 +1,76 @@ +//===-- SARIFDiagnosticPrinter.h - SARIF Diagnostic Client -------*- C++-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This is a concrete diagnostic client, which prints the diagnostics to +// standard error in SARIF format. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_SARIFDIAGNOSTICPRINTER_H +#define LLVM_CLANG_FRONTEND_SARIFDIAGNOSTICPRINTER_H + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/Sarif.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/StringRef.h" +#include + +namespace clang { +class DiagnosticOptions; +class LangOptions; +class SARIFDiagnostic; +class SarifDocumentWriter; + +class SARIFDiagnosticPrinter : public DiagnosticConsumer { +public: + SARIFDiagnosticPrinter(raw_ostream &OS, DiagnosticOptions *Diags); + ~SARIFDiagnosticPrinter() = default; + + SARIFDiagnosticPrinter &operator=(const SARIFDiagnosticPrinter &&) = delete; + SARIFDiagnosticPrinter(SARIFDiagnosticPrinter &&) = delete; + SARIFDiagnosticPrinter &operator=(const SARIFDiagnosticPrinter &) = delete; + SARIFDiagnosticPrinter(const SARIFDiagnosticPrinter &) = delete; + + /// setPrefix - Set the diagnostic printer prefix string, which will be + /// printed at the start of any diagnostics. If empty, no prefix string is + /// used. + void setPrefix(llvm::StringRef Value) { Prefix = Value; } + + bool hasSarifWriter() const { return Writer != nullptr; } + + SarifDocumentWriter &getSarifWriter() const { + assert(Writer && "SarifWriter not set!"); + return *Writer; + } + + void setSarifWriter(std::unique_ptr SarifWriter) { + Writer = std::move(SarifWriter); + } + + void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) override; + void EndSourceFile() override; + void HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) override; + +private: + raw_ostream &OS; + IntrusiveRefCntPtr DiagOpts; + + /// Handle to the currently active SARIF diagnostic emitter. + std::unique_ptr SARIFDiag; + + /// A string to prefix to error messages. + std::string Prefix; + + std::unique_ptr Writer; +}; + +} // end namespace clang + +#endif diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -31,6 +31,8 @@ MultiplexConsumer.cpp PrecompiledPreamble.cpp PrintPreprocessedOutput.cpp + SARIFDiagnostic.cpp + SARIFDiagnosticPrinter.cpp SerializedDiagnosticPrinter.cpp SerializedDiagnosticReader.cpp TestModuleFileExtension.cpp diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -12,6 +12,7 @@ #include "clang/AST/Decl.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LangStandard.h" #include "clang/Basic/SourceManager.h" @@ -25,6 +26,7 @@ #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/Frontend/LogDiagnosticPrinter.h" +#include "clang/Frontend/SARIFDiagnosticPrinter.h" #include "clang/Frontend/SerializedDiagnosticPrinter.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/Utils.h" @@ -346,6 +348,8 @@ // implementing -verify. if (Client) { Diags->setClient(Client, ShouldOwnClient); + } else if (Opts->getFormat() == DiagnosticOptions::SARIF) { + Diags->setClient(new SARIFDiagnosticPrinter(llvm::errs(), Opts)); } else Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts)); diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -11,13 +11,16 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclGroup.h" #include "clang/Basic/Builtins.h" +#include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/LangStandard.h" +#include "clang/Basic/Sarif.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/Frontend/LayoutOverrideSource.h" #include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Frontend/SARIFDiagnosticPrinter.h" #include "clang/Frontend/Utils.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/LiteralSupport.h" @@ -35,6 +38,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" +#include #include using namespace clang; @@ -717,8 +721,14 @@ return false; } } - if (!CI.hasSourceManager()) + if (!CI.hasSourceManager()) { CI.createSourceManager(CI.getFileManager()); + if (CI.getDiagnosticOpts().getFormat() == DiagnosticOptions::SARIF) { + static_cast(&CI.getDiagnosticClient()) + ->setSarifWriter( + std::make_unique(CI.getSourceManager())); + } + } // Set up embedding for any specified files. Do this before we load any // source files, including the primary module map for the compilation. diff --git a/clang/lib/Frontend/SARIFDiagnostic.cpp b/clang/lib/Frontend/SARIFDiagnostic.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Frontend/SARIFDiagnostic.cpp @@ -0,0 +1,226 @@ +//===--------- SARIFDiagnostic.cpp - SARIF Diagnostic Formatting ----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/SARIFDiagnostic.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/Sarif.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Locale.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace clang { + +SARIFDiagnostic::SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts, + DiagnosticOptions *DiagOpts, + SarifDocumentWriter *Writer) + : DiagnosticRenderer(LangOpts, DiagOpts), OS(OS), Writer(Writer) {} + +// FIXME(llvm-project/issues/57323): Refactor Diagnostic classes. +void SARIFDiagnostic::emitDiagnosticMessage( + FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, + StringRef Message, ArrayRef Ranges, + DiagOrStoredDiag D) { + + const auto *Diag = D.dyn_cast(); + + if (!Diag) + return; + + SarifRule Rule = SarifRule::create().setRuleId(std::to_string(Diag->getID())); + + Rule = addDiagnosticLevelToRule(Rule, Level); + + unsigned RuleIdx = Writer->createRule(Rule); + + SarifResult Result = + SarifResult::create(RuleIdx).setDiagnosticMessage(Message); + + if (Loc.isValid()) + Result = addLocationToResult(Result, Loc, PLoc, Ranges, *Diag); + + Writer->appendResult(Result); +} + +SarifResult SARIFDiagnostic::addLocationToResult( + SarifResult Result, FullSourceLoc Loc, PresumedLoc PLoc, + ArrayRef Ranges, const Diagnostic &Diag) { + SmallVector Locations = {}; + + if (PLoc.isInvalid()) { + // At least add the file name if available: + FileID FID = Loc.getFileID(); + if (FID.isValid()) { + if (const FileEntry *FE = Loc.getFileEntry()) { + llvm::StringRef Filename = + emitFilename(FE->getName(), Loc.getManager()); + // FIXME(llvm-project/issues/57366): File-only locations + } + } + return Result; + } + + FileID CaretFileID = Loc.getExpansionLoc().getFileID(); + + for (const CharSourceRange Range : Ranges) { + // Ignore invalid ranges. + if (Range.isInvalid()) + continue; + + auto &SM = Loc.getManager(); + SourceLocation B = SM.getExpansionLoc(Range.getBegin()); + CharSourceRange ERange = SM.getExpansionRange(Range.getEnd()); + SourceLocation E = ERange.getEnd(); + bool IsTokenRange = ERange.isTokenRange(); + + std::pair BInfo = SM.getDecomposedLoc(B); + std::pair EInfo = SM.getDecomposedLoc(E); + + // If the start or end of the range is in another file, just discard + // it. + if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) + continue; + + // Add in the length of the token, so that we cover multi-char + // tokens. + unsigned TokSize = 0; + if (IsTokenRange) + TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts); + + FullSourceLoc BF(B, SM), EF(E, SM); + SourceLocation BeginLoc = SM.translateLineCol( + BF.getFileID(), BF.getLineNumber(), BF.getColumnNumber()); + SourceLocation EndLoc = SM.translateLineCol( + EF.getFileID(), EF.getLineNumber(), EF.getColumnNumber() + TokSize); + + Locations.push_back( + CharSourceRange{SourceRange{BeginLoc, EndLoc}, /* ITR = */ false}); + // FIXME: Additional ranges should use presumed location in both + // Text and SARIF diagnostics. + } + + auto &SM = Loc.getManager(); + auto FID = PLoc.getFileID(); + // Visual Studio 2010 or earlier expects column number to be off by one. + unsigned int ColNo = (LangOpts.MSCompatibilityVersion && + !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012)) + ? PLoc.getColumn() - 1 + : PLoc.getColumn(); + SourceLocation DiagLoc = SM.translateLineCol(FID, PLoc.getLine(), ColNo); + + // FIXME(llvm-project/issues/57366): Properly process #line directives. + Locations.push_back( + CharSourceRange{SourceRange{DiagLoc, DiagLoc}, /* ITR = */ false}); + + return Result.setLocations(Locations); +} + +SarifRule +SARIFDiagnostic::addDiagnosticLevelToRule(SarifRule Rule, + DiagnosticsEngine::Level Level) { + auto Config = SarifReportingConfiguration::create(); + + switch (Level) { + case DiagnosticsEngine::Note: + Config = Config.setLevel(SarifResultLevel::Note); + break; + case DiagnosticsEngine::Remark: + Config = Config.setLevel(SarifResultLevel::None); + break; + case DiagnosticsEngine::Warning: + Config = Config.setLevel(SarifResultLevel::Warning); + break; + case DiagnosticsEngine::Error: + Config = Config.setLevel(SarifResultLevel::Error).setRank(50); + break; + case DiagnosticsEngine::Fatal: + Config = Config.setLevel(SarifResultLevel::Error).setRank(100); + break; + case DiagnosticsEngine::Ignored: + assert(false && "Invalid diagnostic type"); + } + + return Rule.setDefaultConfiguration(Config); +} + +llvm::StringRef SARIFDiagnostic::emitFilename(StringRef Filename, + const SourceManager &SM) { + if (DiagOpts->AbsolutePath) { + llvm::ErrorOr File = + SM.getFileManager().getFile(Filename); + if (File) { + // We want to print a simplified absolute path, i. e. without "dots". + // + // The hardest part here are the paths like "//../". + // On Unix-like systems, we cannot just collapse "/..", because + // paths are resolved sequentially, and, thereby, the path + // "/" may point to a different location. That is why + // we use FileManager::getCanonicalName(), which expands all indirections + // with llvm::sys::fs::real_path() and caches the result. + // + // On the other hand, it would be better to preserve as much of the + // original path as possible, because that helps a user to recognize it. + // real_path() expands all links, which is sometimes too much. Luckily, + // on Windows we can just use llvm::sys::path::remove_dots(), because, + // on that system, both aforementioned paths point to the same place. +#ifdef _WIN32 + SmallString<256> TmpFilename = (*File)->getName(); + llvm::sys::fs::make_absolute(TmpFilename); + llvm::sys::path::native(TmpFilename); + llvm::sys::path::remove_dots(TmpFilename, /* remove_dot_dot */ true); + Filename = StringRef(TmpFilename.data(), TmpFilename.size()); +#else + Filename = SM.getFileManager().getCanonicalName(*File); +#endif + } + } + + return Filename; +} + +/// Print out the file/line/column information and include trace. +/// +/// This method handlen the emission of the diagnostic location information. +/// This includes extracting as much location information as is present for +/// the diagnostic and printing it, as well as any include stack or source +/// ranges necessary. +void SARIFDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef Ranges) { + assert(false && "Not implemented in SARIF mode"); +} + +void SARIFDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) { + assert(false && "Not implemented in SARIF mode"); +} + +void SARIFDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, + StringRef ModuleName) { + assert(false && "Not implemented in SARIF mode"); +} + +void SARIFDiagnostic::emitBuildingModuleLocation(FullSourceLoc Loc, + PresumedLoc PLoc, + StringRef ModuleName) { + assert(false && "Not implemented in SARIF mode"); +} +} // namespace clang diff --git a/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp b/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp @@ -0,0 +1,82 @@ +//===------- SARIFDiagnosticPrinter.cpp - Diagnostic Printer---------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This diagnostic client prints out their diagnostic messages in SARIF format. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/SARIFDiagnosticPrinter.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/Sarif.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/DiagnosticRenderer.h" +#include "clang/Frontend/SARIFDiagnostic.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace clang { + +SARIFDiagnosticPrinter::SARIFDiagnosticPrinter(raw_ostream &OS, + DiagnosticOptions *Diags) + : OS(OS), DiagOpts(Diags) {} + +void SARIFDiagnosticPrinter::BeginSourceFile(const LangOptions &LO, + const Preprocessor *PP) { + // Build the SARIFDiagnostic utility. + assert(hasSarifWriter() && "Writer not set!"); + assert(!SARIFDiag && "SARIFDiagnostic already set."); + SARIFDiag = std::make_unique(OS, LO, &*DiagOpts, &*Writer); + // Initialize the SARIF object. + Writer->createRun("clang", Prefix); +} + +void SARIFDiagnosticPrinter::EndSourceFile() { + assert(SARIFDiag && "SARIFDiagnostic has not been set."); + Writer->endRun(); + llvm::json::Value Value(Writer->createDocument()); + OS << "\n" << Value << "\n\n"; + OS.flush(); +} + +void SARIFDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) { + assert(SARIFDiag && "SARIFDiagnostic has not been set."); + // Default implementation (Warnings/errors count). Keeps track of the + // number of errors. + DiagnosticConsumer::HandleDiagnostic(Level, Info); + + // Render the diagnostic message into a temporary buffer eagerly. We'll use + // this later as we add the diagnostic to the SARIF object. + SmallString<100> OutStr; + Info.FormatDiagnostic(OutStr); + + llvm::raw_svector_ostream DiagMessageStream(OutStr); + + // Use a dedicated, simpler path for diagnostics without a valid location. + // This is important as if the location is missing, we may be emitting + // diagnostics in a context that lacks language options, a source manager, or + // other infrastructure necessary when emitting more rich diagnostics. + if (Info.getLocation().isInvalid()) { + // FIXME: Enable diagnostics without a source manager + return; + } + + // Assert that the rest of our infrastructure is setup properly. + assert(DiagOpts && "Unexpected diagnostic without options set"); + assert(Info.hasSourceManager() && + "Unexpected diagnostic with no source manager"); + + SARIFDiag->emitDiagnostic( + FullSourceLoc(Info.getLocation(), Info.getSourceManager()), Level, + DiagMessageStream.str(), Info.getRanges(), Info.getFixItHints(), &Info); +} +} // namespace clang diff --git a/clang/test/Frontend/sarif-diagnostics.cpp b/clang/test/Frontend/sarif-diagnostics.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/sarif-diagnostics.cpp @@ -0,0 +1,28 @@ +// RUN: %clang -fsyntax-only -Wall -Wextra -fdiagnostics-format=sarif %s > %t 2>&1 || true +// RUN: FileCheck -dump-input=always %s --input-file=%t +// CHECK: warning: diagnostic formatting in SARIF mode is currently unstable [-Wsarif-format-unstable] +// CHECK: {"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length": +// Omit exact length of this file +// CHECK: ,"location":{"index":0,"uri":"file:// +// Omit filepath to llvm project directory +// CHECK: clang/test/Frontend/sarif-diagnostics.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results":[{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":1,"startColumn":1,"startLine":12}}}],"message":{"text":"'main' must return 'int'"},"ruleId":"3465","ruleIndex":0},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":11,"startColumn":11,"startLine":13}}}],"message":{"text":"use of undeclared identifier 'hello'"},"ruleId":"4604","ruleIndex":1},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":17,"startColumn":17,"startLine":15}}}],"message":{"text":"invalid digit 'a' in decimal constant"},"ruleId":"898","ruleIndex":2},{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":5,"startColumn":5,"startLine":19}}}],"message":{"text":"misleading indentation; statement is not part of the previous 'if'"},"ruleId":"1806","ruleIndex":3},{"level":"note","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":3,"startColumn":3,"startLine":17}}}],"message":{"text":"previous statement is here"},"ruleId":"1730","ruleIndex":4},{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":10,"startColumn":10,"startLine":18}}}],"message":{"text":"unused variable 'Yes'"},"ruleId":"6539","ruleIndex":5},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":12,"startColumn":12,"startLine":21}}}],"message":{"text":"use of undeclared identifier 'hi'"},"ruleId":"4604","ruleIndex":6},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":1,"startColumn":1,"startLine":23}}}],"message":{"text":"extraneous closing brace ('}')"},"ruleId":"1399","ruleIndex":7},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":6,"endLine":27,"startColumn":5,"startLine":27}}},{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":10,"endLine":27,"startColumn":9,"startLine":27}}},{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":7,"startColumn":7,"startLine":27}}}],"message":{"text":"invalid operands to binary expression ('t1' and 't1')"},"ruleId":"4539","ruleIndex":8}],"tool":{"driver":{"fullName":"","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"clang","rules":[{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"3465","name":""},{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"4604","name":""},{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"898","name":""},{"defaultConfiguration":{"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":""},"id":"1806","name":""},{"defaultConfiguration":{"enabled":true,"level":"note","rank":-1},"fullDescription":{"text":""},"id":"1730","name":""},{"defaultConfiguration":{"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":""},"id":"6539","name":""},{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"4604","name":""},{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"1399","name":""},{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"4539","name":""}],"version":"16.0.0"}}}],"version":"2.1.0"} +// CHECK: 2 warnings and 6 errors generated. + + +void main() { + int i = hello; + + float test = 1a.0; + + if (true) + bool Yes = true; + return; + + bool j = hi; +} +} + +struct t1 { }; +void f1(t1 x, t1 y) { + x + y; +}