Implementing pragmas
This commit is contained in:
@@ -748,10 +748,11 @@ $TbTestApp = {
|
|||||||
DataSource = Timer
|
DataSource = Timer
|
||||||
Type = uint32
|
Type = uint32
|
||||||
}
|
}
|
||||||
|
//!cast(uint32, uint64): because...
|
||||||
Time = {
|
Time = {
|
||||||
Frequency = 100
|
Frequency = 100
|
||||||
DataSource = Timer
|
DataSource = Timer
|
||||||
Type = uint32
|
Type = uint64
|
||||||
}
|
}
|
||||||
AbsoluteTime = {
|
AbsoluteTime = {
|
||||||
DataSource = Timer
|
DataSource = Timer
|
||||||
@@ -759,6 +760,7 @@ $TbTestApp = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
OutputSignals = {
|
OutputSignals = {
|
||||||
|
//!implicit: defined because....
|
||||||
Counter_DDB1 = {
|
Counter_DDB1 = {
|
||||||
DataSource = DDB1
|
DataSource = DDB1
|
||||||
Type = uint32
|
Type = uint32
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ type ProjectTree struct {
|
|||||||
Root *ProjectNode
|
Root *ProjectNode
|
||||||
References []Reference
|
References []Reference
|
||||||
IsolatedFiles map[string]*ProjectNode
|
IsolatedFiles map[string]*ProjectNode
|
||||||
|
GlobalPragmas map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pt *ProjectTree) ScanDirectory(rootPath string) error {
|
func (pt *ProjectTree) ScanDirectory(rootPath string) error {
|
||||||
@@ -59,6 +60,7 @@ type Fragment struct {
|
|||||||
Definitions []parser.Definition
|
Definitions []parser.Definition
|
||||||
IsObject bool
|
IsObject bool
|
||||||
ObjectPos parser.Position
|
ObjectPos parser.Position
|
||||||
|
EndPos parser.Position
|
||||||
Doc string // Documentation for this fragment (if object)
|
Doc string // Documentation for this fragment (if object)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +71,7 @@ func NewProjectTree() *ProjectTree {
|
|||||||
Metadata: make(map[string]string),
|
Metadata: make(map[string]string),
|
||||||
},
|
},
|
||||||
IsolatedFiles: make(map[string]*ProjectNode),
|
IsolatedFiles: make(map[string]*ProjectNode),
|
||||||
|
GlobalPragmas: make(map[string][]string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +92,7 @@ func (pt *ProjectTree) RemoveFile(file string) {
|
|||||||
pt.References = newRefs
|
pt.References = newRefs
|
||||||
|
|
||||||
delete(pt.IsolatedFiles, file)
|
delete(pt.IsolatedFiles, file)
|
||||||
|
delete(pt.GlobalPragmas, file)
|
||||||
pt.removeFileFromNode(pt.Root, file)
|
pt.removeFileFromNode(pt.Root, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +160,14 @@ func (pt *ProjectTree) extractFieldMetadata(node *ProjectNode, f *parser.Field)
|
|||||||
func (pt *ProjectTree) AddFile(file string, config *parser.Configuration) {
|
func (pt *ProjectTree) AddFile(file string, config *parser.Configuration) {
|
||||||
pt.RemoveFile(file)
|
pt.RemoveFile(file)
|
||||||
|
|
||||||
|
// Collect global pragmas
|
||||||
|
for _, p := range config.Pragmas {
|
||||||
|
txt := strings.TrimSpace(p.Text)
|
||||||
|
if strings.HasPrefix(txt, "//!allow(") {
|
||||||
|
pt.GlobalPragmas[file] = append(pt.GlobalPragmas[file], txt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if config.Package == nil {
|
if config.Package == nil {
|
||||||
node := &ProjectNode{
|
node := &ProjectNode{
|
||||||
Children: make(map[string]*ProjectNode),
|
Children: make(map[string]*ProjectNode),
|
||||||
@@ -249,6 +261,7 @@ func (pt *ProjectTree) addObjectFragment(node *ProjectNode, file string, obj *pa
|
|||||||
File: file,
|
File: file,
|
||||||
IsObject: true,
|
IsObject: true,
|
||||||
ObjectPos: obj.Position,
|
ObjectPos: obj.Position,
|
||||||
|
EndPos: obj.Subnode.EndPosition,
|
||||||
Doc: doc,
|
Doc: doc,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,3 +475,44 @@ func (pt *ProjectTree) queryNode(node *ProjectNode, file string, line, col int)
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pt *ProjectTree) GetNodeContaining(file string, pos parser.Position) *ProjectNode {
|
||||||
|
if isoNode, ok := pt.IsolatedFiles[file]; ok {
|
||||||
|
if found := pt.findNodeContaining(isoNode, file, pos); found != nil {
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
return isoNode
|
||||||
|
}
|
||||||
|
if pt.Root != nil {
|
||||||
|
if found := pt.findNodeContaining(pt.Root, file, pos); found != nil {
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
for _, frag := range pt.Root.Fragments {
|
||||||
|
if frag.File == file && !frag.IsObject {
|
||||||
|
return pt.Root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *ProjectTree) findNodeContaining(node *ProjectNode, file string, pos parser.Position) *ProjectNode {
|
||||||
|
for _, child := range node.Children {
|
||||||
|
if res := pt.findNodeContaining(child, file, pos); res != nil {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, frag := range node.Fragments {
|
||||||
|
if frag.File == file && frag.IsObject {
|
||||||
|
start := frag.ObjectPos
|
||||||
|
end := frag.EndPos
|
||||||
|
|
||||||
|
if (pos.Line > start.Line || (pos.Line == start.Line && pos.Column >= start.Column)) &&
|
||||||
|
(pos.Line < end.Line || (pos.Line == end.Line && pos.Column <= end.Column)) {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -581,8 +581,8 @@ func formatNodeInfo(node *index.ProjectNode) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Size
|
// Size
|
||||||
dims := node.Metadata["NumberOfDimensions"]
|
dims := node.Metadata["NumberOfDimensions"]
|
||||||
elems := node.Metadata["NumberOfElements"]
|
elems := node.Metadata["NumberOfElements"]
|
||||||
if dims != "" || elems != "" {
|
if dims != "" || elems != "" {
|
||||||
sigInfo += fmt.Sprintf("**Size**: `[%s]`, `%s` dims ", elems, dims)
|
sigInfo += fmt.Sprintf("**Size**: `[%s]`, `%s` dims ", elems, dims)
|
||||||
}
|
}
|
||||||
@@ -592,6 +592,57 @@ elems := node.Metadata["NumberOfElements"]
|
|||||||
if node.Doc != "" {
|
if node.Doc != "" {
|
||||||
info += fmt.Sprintf("\n\n%s", node.Doc)
|
info += fmt.Sprintf("\n\n%s", node.Doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find references
|
||||||
|
var refs []string
|
||||||
|
for _, ref := range tree.References {
|
||||||
|
if ref.Target == node {
|
||||||
|
container := tree.GetNodeContaining(ref.File, ref.Position)
|
||||||
|
if container != nil {
|
||||||
|
threadName := ""
|
||||||
|
stateName := ""
|
||||||
|
|
||||||
|
curr := container
|
||||||
|
for curr != nil {
|
||||||
|
if cls, ok := curr.Metadata["Class"]; ok {
|
||||||
|
if cls == "RealTimeThread" {
|
||||||
|
threadName = curr.RealName
|
||||||
|
}
|
||||||
|
if cls == "RealTimeState" {
|
||||||
|
stateName = curr.RealName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curr = curr.Parent
|
||||||
|
}
|
||||||
|
|
||||||
|
if threadName != "" || stateName != "" {
|
||||||
|
refStr := ""
|
||||||
|
if stateName != "" {
|
||||||
|
refStr += fmt.Sprintf("State: `%s`", stateName)
|
||||||
|
}
|
||||||
|
if threadName != "" {
|
||||||
|
if refStr != "" {
|
||||||
|
refStr += ", "
|
||||||
|
}
|
||||||
|
refStr += fmt.Sprintf("Thread: `%s`", threadName)
|
||||||
|
}
|
||||||
|
refs = append(refs, refStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(refs) > 0 {
|
||||||
|
uniqueRefs := make(map[string]bool)
|
||||||
|
info += "\n\n**Referenced in**:\n"
|
||||||
|
for _, r := range refs {
|
||||||
|
if !uniqueRefs[r] {
|
||||||
|
uniqueRefs[r] = true
|
||||||
|
info += fmt.Sprintf("- %s\n", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -354,15 +354,17 @@ func (v *Validator) validateGAMSignal(gamNode, signalNode *index.ProjectNode, di
|
|||||||
}
|
}
|
||||||
|
|
||||||
if targetNode == nil {
|
if targetNode == nil {
|
||||||
suppress := false
|
suppressed := v.isGloballyAllowed("implicit")
|
||||||
for _, p := range signalNode.Pragmas {
|
if !suppressed {
|
||||||
if strings.HasPrefix(p, "implicit:") {
|
for _, p := range signalNode.Pragmas {
|
||||||
suppress = true
|
if strings.HasPrefix(p, "implicit:") {
|
||||||
break
|
suppressed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !suppress {
|
if !suppressed {
|
||||||
v.Diagnostics = append(v.Diagnostics, Diagnostic{
|
v.Diagnostics = append(v.Diagnostics, Diagnostic{
|
||||||
Level: LevelWarning,
|
Level: LevelWarning,
|
||||||
Message: fmt.Sprintf("Implicitly Defined Signal: '%s' is defined in GAM '%s' but not in DataSource '%s'", targetSignalName, gamNode.RealName, dsName),
|
Message: fmt.Sprintf("Implicitly Defined Signal: '%s' is defined in GAM '%s' but not in DataSource '%s'", targetSignalName, gamNode.RealName, dsName),
|
||||||
@@ -624,6 +626,9 @@ func (v *Validator) checkUnusedRecursive(node *index.ProjectNode, referenced map
|
|||||||
// Heuristic for GAM
|
// Heuristic for GAM
|
||||||
if isGAM(node) {
|
if isGAM(node) {
|
||||||
if !referenced[node] {
|
if !referenced[node] {
|
||||||
|
if v.isGloballyAllowed("unused") {
|
||||||
|
return
|
||||||
|
}
|
||||||
suppress := false
|
suppress := false
|
||||||
for _, p := range node.Pragmas {
|
for _, p := range node.Pragmas {
|
||||||
if strings.HasPrefix(p, "unused:") {
|
if strings.HasPrefix(p, "unused:") {
|
||||||
@@ -647,6 +652,9 @@ func (v *Validator) checkUnusedRecursive(node *index.ProjectNode, referenced map
|
|||||||
if signalsNode, ok := node.Children["Signals"]; ok {
|
if signalsNode, ok := node.Children["Signals"]; ok {
|
||||||
for _, signal := range signalsNode.Children {
|
for _, signal := range signalsNode.Children {
|
||||||
if !referenced[signal] {
|
if !referenced[signal] {
|
||||||
|
if v.isGloballyAllowed("unused") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
suppress := false
|
suppress := false
|
||||||
for _, p := range signal.Pragmas {
|
for _, p := range signal.Pragmas {
|
||||||
if strings.HasPrefix(p, "unused:") {
|
if strings.HasPrefix(p, "unused:") {
|
||||||
@@ -709,4 +717,16 @@ func (v *Validator) getNodeFile(node *index.ProjectNode) string {
|
|||||||
return node.Fragments[0].File
|
return node.Fragments[0].File
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) isGloballyAllowed(warningType string) bool {
|
||||||
|
prefix := fmt.Sprintf("//!allow(%s)", warningType)
|
||||||
|
for _, pragmas := range v.Tree.GlobalPragmas {
|
||||||
|
for _, p := range pragmas {
|
||||||
|
if strings.HasPrefix(p, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,11 @@ The LSP server should provide the following capabilities:
|
|||||||
- **Nodes (`+` / `$`)**: The prefixes `+` and `$` indicate that the node represents an object.
|
- **Nodes (`+` / `$`)**: The prefixes `+` and `$` indicate that the node represents an object.
|
||||||
- **Constraint**: These nodes _must_ contain a field named `Class` within their subnode definition (across all files where the node is defined).
|
- **Constraint**: These nodes _must_ contain a field named `Class` within their subnode definition (across all files where the node is defined).
|
||||||
- **Signals**: Signals are considered nodes but **not** objects. They do not require a `Class` field.
|
- **Signals**: Signals are considered nodes but **not** objects. They do not require a `Class` field.
|
||||||
- **Pragmas (`//!`)**: Used to suppress specific diagnostics. The developer can use these to explain why a rule is being ignored.
|
- **Pragmas (`//!`)**: Used to suppress specific diagnostics. The developer can use these to explain why a rule is being ignored. Supported pragmas:
|
||||||
|
- `//!unused: REASON` - Suppress "Unused GAM" or "Unused Signal" warnings for a specific node.
|
||||||
|
- `//!implicit: REASON` - Suppress "Implicitly Defined Signal" warnings for a specific signal reference.
|
||||||
|
- `//!allow(WARNING_TYPE): REASON` - Global suppression for a specific warning type across the whole project (supported: `unused`, `implicit`).
|
||||||
|
- `//!cast(DEF_TYPE, CUR_TYPE): REASON` - Suppress "Type Inconsistency" errors if types match.
|
||||||
- **Structure**: A configuration is composed by one or more definitions.
|
- **Structure**: A configuration is composed by one or more definitions.
|
||||||
|
|
||||||
### Core MARTe Classes
|
### Core MARTe Classes
|
||||||
@@ -105,6 +109,7 @@ MARTe configurations typically involve several main categories of objects:
|
|||||||
- **Requirements**:
|
- **Requirements**:
|
||||||
- All signal definitions **must** include a `Type` field with a valid value.
|
- All signal definitions **must** include a `Type` field with a valid value.
|
||||||
- **Size Information**: Signals can optionally include `NumberOfDimensions` and `NumberOfElements` fields. If not explicitly defined, these default to `1`.
|
- **Size Information**: Signals can optionally include `NumberOfDimensions` and `NumberOfElements` fields. If not explicitly defined, these default to `1`.
|
||||||
|
- **Property Matching**: Signal references in GAMs must match the properties (`Type`, `NumberOfElements`, `NumberOfDimensions`) of the defined signal in the `DataSource`.
|
||||||
- **Extensibility**: Signal definitions can include additional fields as required by the specific application context.
|
- **Extensibility**: Signal definitions can include additional fields as required by the specific application context.
|
||||||
- **Signal Reference Syntax**:
|
- **Signal Reference Syntax**:
|
||||||
- Signals are referenced or defined in `InputSignals` or `OutputSignals` sub-nodes using one of the following formats:
|
- Signals are referenced or defined in `InputSignals` or `OutputSignals` sub-nodes using one of the following formats:
|
||||||
@@ -186,13 +191,14 @@ The `fmt` command must format the code according to the following rules:
|
|||||||
The LSP and `check` command should report the following:
|
The LSP and `check` command should report the following:
|
||||||
|
|
||||||
- **Warnings**:
|
- **Warnings**:
|
||||||
- **Unused GAM**: A GAM is defined but not referenced in any thread or scheduler.
|
- **Unused GAM**: A GAM is defined but not referenced in any thread or scheduler. (Suppress with `//!unused`)
|
||||||
- **Unused Signal**: A signal is explicitly defined in a `DataSource` but never referenced in any `GAM`.
|
- **Unused Signal**: A signal is explicitly defined in a `DataSource` but never referenced in any `GAM`. (Suppress with `//!unused`)
|
||||||
- **Implicitly Defined Signal**: A signal is defined only within a `GAM` and not in its parent `DataSource`.
|
- **Implicitly Defined Signal**: A signal is defined only within a `GAM` and not in its parent `DataSource`. (Suppress with `//!implicit`)
|
||||||
|
|
||||||
- **Errors**:
|
- **Errors**:
|
||||||
- **Type Inconsistency**: A signal is referenced with a type different from its definition.
|
- **Type Inconsistency**: A signal is referenced with a type different from its definition. (Suppress with `//!cast`)
|
||||||
- **Size Inconsistency**: A signal is referenced with a size (dimensions/elements) different from its definition.
|
- **Size Inconsistency**: A signal is referenced with a size (dimensions/elements) different from its definition.
|
||||||
|
- **Invalid Signal Content**: The `Signals` container of a `DataSource` contains invalid elements (e.g., fields instead of nodes).
|
||||||
- **Duplicate Field Definition**: A field is defined multiple times within the same node scope (including across multiple files).
|
- **Duplicate Field Definition**: A field is defined multiple times within the same node scope (including across multiple files).
|
||||||
- **Validation Errors**:
|
- **Validation Errors**:
|
||||||
- Missing mandatory fields.
|
- Missing mandatory fields.
|
||||||
|
|||||||
73
test/lsp_hover_context_test.go
Normal file
73
test/lsp_hover_context_test.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/index"
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetNodeContaining(t *testing.T) {
|
||||||
|
content := `
|
||||||
|
+App = {
|
||||||
|
Class = RealTimeApplication
|
||||||
|
+State1 = {
|
||||||
|
Class = RealTimeState
|
||||||
|
+Thread1 = {
|
||||||
|
Class = RealTimeThread
|
||||||
|
Functions = { GAM1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+GAM1 = { Class = IOGAM }
|
||||||
|
`
|
||||||
|
p := parser.NewParser(content)
|
||||||
|
config, err := p.Parse()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := index.NewProjectTree()
|
||||||
|
file := "hover_context.marte"
|
||||||
|
idx.AddFile(file, config)
|
||||||
|
idx.ResolveReferences()
|
||||||
|
|
||||||
|
// Find reference to GAM1
|
||||||
|
var gamRef *index.Reference
|
||||||
|
for i := range idx.References {
|
||||||
|
ref := &idx.References[i]
|
||||||
|
if ref.Name == "GAM1" {
|
||||||
|
gamRef = ref
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gamRef == nil {
|
||||||
|
t.Fatal("Reference to GAM1 not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check containing node
|
||||||
|
container := idx.GetNodeContaining(file, gamRef.Position)
|
||||||
|
if container == nil {
|
||||||
|
t.Fatal("Container not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if container.RealName != "+Thread1" {
|
||||||
|
t.Errorf("Expected container +Thread1, got %s", container.RealName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check traversal up to State
|
||||||
|
curr := container
|
||||||
|
foundState := false
|
||||||
|
for curr != nil {
|
||||||
|
if curr.RealName == "+State1" {
|
||||||
|
foundState = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
curr = curr.Parent
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundState {
|
||||||
|
t.Error("State parent not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
67
test/validator_global_pragma_test.go
Normal file
67
test/validator_global_pragma_test.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/index"
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/parser"
|
||||||
|
"github.com/marte-dev/marte-dev-tools/internal/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGlobalPragma(t *testing.T) {
|
||||||
|
content := `
|
||||||
|
//!allow(unused): Suppress all unused
|
||||||
|
//!allow(implicit): Suppress all implicit
|
||||||
|
|
||||||
|
+Data = {
|
||||||
|
Class = ReferenceContainer
|
||||||
|
+MyDS = {
|
||||||
|
Class = FileReader
|
||||||
|
Filename = "test"
|
||||||
|
Signals = {
|
||||||
|
UnusedSig = { Type = uint32 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+MyGAM = {
|
||||||
|
Class = IOGAM
|
||||||
|
InputSignals = {
|
||||||
|
ImplicitSig = { DataSource = MyDS Type = uint32 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
p := parser.NewParser(content)
|
||||||
|
config, err := p.Parse()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := index.NewProjectTree()
|
||||||
|
idx.AddFile("global_pragma.marte", config)
|
||||||
|
idx.ResolveReferences()
|
||||||
|
|
||||||
|
v := validator.NewValidator(idx, ".")
|
||||||
|
v.ValidateProject()
|
||||||
|
v.CheckUnused()
|
||||||
|
|
||||||
|
foundUnusedWarning := false
|
||||||
|
foundImplicitWarning := false
|
||||||
|
|
||||||
|
for _, d := range v.Diagnostics {
|
||||||
|
if strings.Contains(d.Message, "Unused Signal") {
|
||||||
|
foundUnusedWarning = true
|
||||||
|
}
|
||||||
|
if strings.Contains(d.Message, "Implicitly Defined Signal") {
|
||||||
|
foundImplicitWarning = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundUnusedWarning {
|
||||||
|
t.Error("Expected warning for UnusedSig to be suppressed globally")
|
||||||
|
}
|
||||||
|
if foundImplicitWarning {
|
||||||
|
t.Error("Expected warning for ImplicitSig to be suppressed globally")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user