diff --git a/swift/extractor/infra/SwiftDiagnosticKind.h b/swift/extractor/infra/SwiftDiagnosticKind.h new file mode 100644 index 00000000000..6558af4e873 --- /dev/null +++ b/swift/extractor/infra/SwiftDiagnosticKind.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace codeql { + +inline int translateDiagnosticsKind(swift::DiagnosticKind kind) { + using Kind = swift::DiagnosticKind; + switch (kind) { + case Kind::Error: + return 1; + case Kind::Warning: + return 2; + case Kind::Note: + return 3; + case Kind::Remark: + return 4; + default: + return 0; + } +} + +} // namespace codeql diff --git a/swift/extractor/invocation/SwiftDiagnosticsConsumer.cpp b/swift/extractor/invocation/SwiftDiagnosticsConsumer.cpp index 8618ef7f3c7..5ac875a1e1e 100644 --- a/swift/extractor/invocation/SwiftDiagnosticsConsumer.cpp +++ b/swift/extractor/invocation/SwiftDiagnosticsConsumer.cpp @@ -1,6 +1,7 @@ #include "swift/extractor/invocation/SwiftDiagnosticsConsumer.h" #include "swift/extractor/trap/generated/TrapEntries.h" #include "swift/extractor/trap/TrapDomain.h" +#include "swift/extractor/infra/SwiftDiagnosticKind.h" #include #include @@ -10,26 +11,12 @@ using namespace codeql; -static int diagnosticsKind(const swift::DiagnosticInfo& diagInfo) { - switch (diagInfo.Kind) { - case swift::DiagnosticKind::Error: - return 1; - case swift::DiagnosticKind::Warning: - return 2; - case swift::DiagnosticKind::Note: - return 3; - case swift::DiagnosticKind::Remark: - return 4; - } - return 0; -} - void SwiftDiagnosticsConsumer::handleDiagnostic(swift::SourceManager& sourceManager, const swift::DiagnosticInfo& diagInfo) { auto message = getDiagMessage(sourceManager, diagInfo); DiagnosticsTrap diag{}; diag.id = trap.createLabel(); - diag.kind = diagnosticsKind(diagInfo); + diag.kind = translateDiagnosticsKind(diagInfo.Kind); diag.text = message; trap.emit(diag); locationExtractor.attachLocation(sourceManager, diagInfo.Loc, diagInfo.Loc, diag.id); diff --git a/swift/extractor/translators/DeclTranslator.cpp b/swift/extractor/translators/DeclTranslator.cpp index 500dabe7224..9e9b6009703 100644 --- a/swift/extractor/translators/DeclTranslator.cpp +++ b/swift/extractor/translators/DeclTranslator.cpp @@ -2,6 +2,7 @@ #include #include +#include "swift/extractor/infra/SwiftDiagnosticKind.h" namespace codeql { namespace { @@ -401,4 +402,18 @@ std::optional DeclTranslator::translateOpaqueTypeDecl( return std::nullopt; } +codeql::PoundDiagnosticDecl DeclTranslator::translatePoundDiagnosticDecl( + const swift::PoundDiagnosticDecl& decl) { + auto entry = createEntry(decl); + entry.kind = translateDiagnosticsKind(decl.getKind()); + entry.message = dispatcher.fetchLabel(decl.getMessage()); + return entry; +} + +codeql::MissingMemberDecl DeclTranslator::translateMissingMemberDecl( + const swift::MissingMemberDecl& decl) { + auto entry = createEntry(decl); + entry.name = decl.getName().getBaseName().userFacingName().str(); + return entry; +} } // namespace codeql diff --git a/swift/extractor/translators/DeclTranslator.h b/swift/extractor/translators/DeclTranslator.h index e2f69a0e247..f72a29437d5 100644 --- a/swift/extractor/translators/DeclTranslator.h +++ b/swift/extractor/translators/DeclTranslator.h @@ -46,6 +46,8 @@ class DeclTranslator : public AstTranslatorBase { std::optional translateModuleDecl(const swift::ModuleDecl& decl); codeql::IfConfigDecl translateIfConfigDecl(const swift::IfConfigDecl& decl); std::optional translateOpaqueTypeDecl(const swift::OpaqueTypeDecl& decl); + codeql::PoundDiagnosticDecl translatePoundDiagnosticDecl(const swift::PoundDiagnosticDecl& decl); + codeql::MissingMemberDecl translateMissingMemberDecl(const swift::MissingMemberDecl& decl); private: std::string mangledName(const swift::ValueDecl& decl); diff --git a/swift/ql/lib/codeql/swift/elements/decl/MissingMemberDecl.qll b/swift/ql/lib/codeql/swift/elements/decl/MissingMemberDecl.qll index 9c69eff103c..195dd15c368 100644 --- a/swift/ql/lib/codeql/swift/elements/decl/MissingMemberDecl.qll +++ b/swift/ql/lib/codeql/swift/elements/decl/MissingMemberDecl.qll @@ -1,4 +1,5 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file private import codeql.swift.generated.decl.MissingMemberDecl -class MissingMemberDecl extends Generated::MissingMemberDecl { } +class MissingMemberDecl extends Generated::MissingMemberDecl { + override string toString() { result = this.getName() + " (missing)" } +} diff --git a/swift/ql/lib/codeql/swift/elements/decl/PoundDiagnosticDecl.qll b/swift/ql/lib/codeql/swift/elements/decl/PoundDiagnosticDecl.qll index a6e0f43a378..aa7c5f86c0e 100644 --- a/swift/ql/lib/codeql/swift/elements/decl/PoundDiagnosticDecl.qll +++ b/swift/ql/lib/codeql/swift/elements/decl/PoundDiagnosticDecl.qll @@ -2,6 +2,12 @@ private import codeql.swift.generated.decl.PoundDiagnosticDecl class PoundDiagnosticDecl extends Generated::PoundDiagnosticDecl { override string toString() { - result = "#..." // TODO: Once we extract whether this is an error or a warning we can improve this. + this.isError() and result = "#error(...)" + or + this.isWarning() and result = "#warning(...)" } + + predicate isError() { this.getKind() = 1 } + + predicate isWarning() { this.getKind() = 2 } } diff --git a/swift/ql/lib/codeql/swift/generated/ParentChild.qll b/swift/ql/lib/codeql/swift/generated/ParentChild.qll index 1200c82a13a..64b1e4db8a8 100644 --- a/swift/ql/lib/codeql/swift/generated/ParentChild.qll +++ b/swift/ql/lib/codeql/swift/generated/ParentChild.qll @@ -373,14 +373,17 @@ private module Impl { private Element getImmediateChildOfPoundDiagnosticDecl( PoundDiagnosticDecl e, int index, string partialPredicateCall ) { - exists(int b, int bDecl, int n | + exists(int b, int bDecl, int n, int nMessage | b = 0 and bDecl = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfDecl(e, i, _)) | i) and n = bDecl and + nMessage = n + 1 and ( none() or result = getImmediateChildOfDecl(e, index - b, partialPredicateCall) + or + index = n and result = e.getImmediateMessage() and partialPredicateCall = "Message()" ) ) } diff --git a/swift/ql/lib/codeql/swift/generated/Raw.qll b/swift/ql/lib/codeql/swift/generated/Raw.qll index 2a3402be6e1..19973a2b91a 100644 --- a/swift/ql/lib/codeql/swift/generated/Raw.qll +++ b/swift/ql/lib/codeql/swift/generated/Raw.qll @@ -115,6 +115,8 @@ module Raw { class MissingMemberDecl extends @missing_member_decl, Decl { override string toString() { result = "MissingMemberDecl" } + + string getName() { missing_member_decls(this, result) } } class OperatorDecl extends @operator_decl, Decl { @@ -131,6 +133,10 @@ module Raw { class PoundDiagnosticDecl extends @pound_diagnostic_decl, Decl { override string toString() { result = "PoundDiagnosticDecl" } + + int getKind() { pound_diagnostic_decls(this, result, _) } + + StringLiteralExpr getMessage() { pound_diagnostic_decls(this, _, result) } } class PrecedenceGroupDecl extends @precedence_group_decl, Decl { diff --git a/swift/ql/lib/codeql/swift/generated/decl/MissingMemberDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/MissingMemberDecl.qll index df1377a7fec..b5a026b1325 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/MissingMemberDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/MissingMemberDecl.qll @@ -4,7 +4,17 @@ private import codeql.swift.generated.Raw import codeql.swift.elements.decl.Decl module Generated { + /** + * A placeholder for missing declarations that can arise on object deserialization. + */ class MissingMemberDecl extends Synth::TMissingMemberDecl, Decl { override string getAPrimaryQlClass() { result = "MissingMemberDecl" } + + /** + * Gets the name of this missing member declaration. + */ + string getName() { + result = Synth::convertMissingMemberDeclToRaw(this).(Raw::MissingMemberDecl).getName() + } } } diff --git a/swift/ql/lib/codeql/swift/generated/decl/PoundDiagnosticDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/PoundDiagnosticDecl.qll index efa71409638..1aec8016d81 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/PoundDiagnosticDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/PoundDiagnosticDecl.qll @@ -2,9 +2,38 @@ private import codeql.swift.generated.Synth private import codeql.swift.generated.Raw import codeql.swift.elements.decl.Decl +import codeql.swift.elements.expr.StringLiteralExpr module Generated { + /** + * A diagnostic directive, which is either `#error` or `#warning`. + */ class PoundDiagnosticDecl extends Synth::TPoundDiagnosticDecl, Decl { override string getAPrimaryQlClass() { result = "PoundDiagnosticDecl" } + + /** + * Gets the This is 1 for `#error` and 2 for `#warning`. + */ + int getKind() { + result = Synth::convertPoundDiagnosticDeclToRaw(this).(Raw::PoundDiagnosticDecl).getKind() + } + + /** + * Gets the message of this pound diagnostic declaration. + * + * This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the + * behavior of both the `Immediate` and non-`Immediate` versions. + */ + StringLiteralExpr getImmediateMessage() { + result = + Synth::convertStringLiteralExprFromRaw(Synth::convertPoundDiagnosticDeclToRaw(this) + .(Raw::PoundDiagnosticDecl) + .getMessage()) + } + + /** + * Gets the message of this pound diagnostic declaration. + */ + final StringLiteralExpr getMessage() { result = getImmediateMessage().resolve() } } } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index 736937d1cd8..f543c5d4838 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -254,7 +254,8 @@ import_decl_declarations( //dir=decl ); missing_member_decls( //dir=decl - unique int id: @missing_member_decl + unique int id: @missing_member_decl, + string name: string ref ); @operator_decl = @@ -288,7 +289,9 @@ pattern_binding_decl_patterns( //dir=decl ); pound_diagnostic_decls( //dir=decl - unique int id: @pound_diagnostic_decl + unique int id: @pound_diagnostic_decl, + int kind: int ref, + int message: @string_literal_expr_or_none ref ); precedence_group_decls( //dir=decl @@ -2450,6 +2453,11 @@ variadic_sequence_types( //dir=type | @unspecified_element ; +@string_literal_expr_or_none = + @string_literal_expr +| @unspecified_element +; + @tap_expr_or_none = @tap_expr | @unspecified_element diff --git a/swift/ql/test/extractor-tests/generated/decl/MissingMemberDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/decl/MissingMemberDecl/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/MissingMemberDecl/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/PoundDiagnosticDecl.expected b/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/PoundDiagnosticDecl.expected new file mode 100644 index 00000000000..64e6c69a075 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/PoundDiagnosticDecl.expected @@ -0,0 +1,2 @@ +| diagnostics.swift:2:1:2:25 | #warning(...) | getModule: | file://:0:0:0:0 | diagnostics | getKind: | 2 | getMessage: | diagnostics.swift:2:10:2:10 | I'm a warning | +| diagnostics.swift:3:1:3:26 | #error(...) | getModule: | file://:0:0:0:0 | diagnostics | getKind: | 1 | getMessage: | diagnostics.swift:3:8:3:8 | And I'm an error | diff --git a/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/PoundDiagnosticDecl.ql b/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/PoundDiagnosticDecl.ql new file mode 100644 index 00000000000..f5cbb62ed1d --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/PoundDiagnosticDecl.ql @@ -0,0 +1,12 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from PoundDiagnosticDecl x, ModuleDecl getModule, int getKind, StringLiteralExpr getMessage +where + toBeTested(x) and + not x.isUnknown() and + getModule = x.getModule() and + getKind = x.getKind() and + getMessage = x.getMessage() +select x, "getModule:", getModule, "getKind:", getKind, "getMessage:", getMessage diff --git a/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/diagnostics.swift b/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/diagnostics.swift new file mode 100644 index 00000000000..2b663ab745d --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/PoundDiagnosticDecl/diagnostics.swift @@ -0,0 +1,3 @@ +//codeql-extractor-expected-status: 1 +#warning("I'm a warning") +#error("And I'm an error") diff --git a/swift/schema.py b/swift/schema.py index 251b6207126..7956e01dd89 100644 --- a/swift/schema.py +++ b/swift/schema.py @@ -112,8 +112,10 @@ class ImportDecl(Decl): imported_module: optional["ModuleDecl"] declarations: list["ValueDecl"] +@qltest.skip class MissingMemberDecl(Decl): - pass + """A placeholder for missing declarations that can arise on object deserialization.""" + name: string class OperatorDecl(Decl): name: string @@ -123,7 +125,9 @@ class PatternBindingDecl(Decl): patterns: list[Pattern] | child class PoundDiagnosticDecl(Decl): - pass + """ A diagnostic directive, which is either `#error` or `#warning`.""" + kind: int | doc("""This is 1 for `#error` and 2 for `#warning`""") + message: "StringLiteralExpr" | child class PrecedenceGroupDecl(Decl): pass